Forums

Sega Master System / Mark III / Game Gear
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew

View topic - Good starting thread for new developers!

Reply to topic
Author Message
  • Joined: 30 Apr 2024
  • Posts: 8
Reply with quote
Good starting thread for new developers!
Post Posted: Wed May 01, 2024 8:09 pm
Hi!
I've been through some tutorials here and provided by Chibiakumas for game gear development and it's been grand!
I've hit issues I am positive are just normal new developer issues regarding the main loop and absolute garbage appearing on my screen.
Given my assumptions (I am new, I have made a glaringly simple error) I was wondering if there was a thread to read through for new developers so I can try to self diagnose and fix my issue before I start a specific help thread.

Thanks!
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14756
  • Location: London
Reply with quote
Post Posted: Wed May 01, 2024 8:14 pm
Debugging mostly consists of two steps: figure out where it goes wrong, and figure out why. Use an emulator with a debugger and try stepping through the code to isolate where it goes wrong.
  View user's profile Send private message Visit poster's website
  • Joined: 30 Apr 2024
  • Posts: 8
Reply with quote
Post Posted: Wed May 01, 2024 9:50 pm
Of course of course. I’m using Meka for all of that.
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 3858
  • Location: Stockholm, Sweden
Reply with quote
Post Posted: Thu May 02, 2024 9:37 am
Emulicious is a great (likely the best?) emulator for developers - its debug features are unrivaled. Try it and see it :D
  View user's profile Send private message Visit poster's website
  • Joined: 27 Apr 2021
  • Posts: 41
Reply with quote
Post Posted: Thu May 02, 2024 9:40 am
Emulicious has also a VSCode extension that make debugging waaaaay more easy :)
  View user's profile Send private message
  • Joined: 30 Apr 2024
  • Posts: 8
Reply with quote
Post Posted: Thu May 02, 2024 4:11 pm
Oddly enough: Emulicious is the only 1 of 3 (Meka, Fusion, Emulicious) that doesn't load my rom -_-. I'll have to look into it in the future. I assume I am missing headers or footers.
  View user's profile Send private message Visit poster's website
  • Joined: 06 Mar 2022
  • Posts: 683
  • Location: London, UK
Reply with quote
Post Posted: Thu May 02, 2024 4:33 pm
the60ftatomicman wrote
Oddly enough: Emulicious is the only 1 of 3 (Meka, Fusion, Emulicious) that doesn't load my rom -_-. I'll have to look into it in the future. I assume I am missing headers or footers.

If it doesn't load your ROM it's almost certainly because real hardware wouldn't - it's extremely accurate with default settings.
  View user's profile Send private message Visit poster's website
  • Joined: 30 Apr 2024
  • Posts: 8
Reply with quote
Post Posted: Fri May 03, 2024 2:14 am
AH OK it was my bad, did not initialize vram and garbage abound was due to all my values being randomly assigned in emulicious ( you can turn that off in the emu but it's better im sure to just to blank the vram)

Is everyone using WLA-dx?

Originally I started with the Chibi Akumas tutorial and it used VASM which admittedly seems a tad easier to begin with. However those forums are a bit less active from what I can tell than the SMS power forums; so im kinda brute forcing myself to learn to compile with WLA-dx and its currently a bear of small onesy twosy fixes.
  View user's profile Send private message Visit poster's website
  • Joined: 01 Feb 2014
  • Posts: 885
Reply with quote
Post Posted: Fri May 03, 2024 2:49 pm
the60ftatomicman wrote
Originally I started with the Chibi Akumas tutorial and it used VASM which admittedly seems a tad easier to begin with.

I’ve never used VASM. What is the difference?
  View user's profile Send private message
  • Joined: 30 Apr 2024
  • Posts: 8
Reply with quote
Post Posted: Thu May 09, 2024 7:11 pm
Realistically, between screen redraws how many tiles seems to be "too many" BG tiles to update?

I have a small script at the moment that I was expecting to update about 108 tiles (VRAM address is accesseed by looping a small section in ram where I keep the bytes in neighboring addresses) and that causes an odd flickering.

It's seemingly a 5th of the screen so I get it if that's a bit too much but wanted to ask the experts.

(Why?) I want to have more than 8 characters on a line so this seemed like an easy way to do it.
  View user's profile Send private message Visit poster's website
  • Joined: 06 Mar 2022
  • Posts: 683
  • Location: London, UK
Reply with quote
Post Posted: Thu May 09, 2024 8:05 pm
Kind of depends on a lot of things.
When you say you are "updating" BG tiles, do you mean writing to the tilemap or modifying the tile data itself (often described as "streaming" here)?

Speaking generally, there are two different rates you can write data to the VDP: during active display you are limited to something like one byte every 29 T-states (CPU cycles). During vertical blanking & border the limit is less stringent and this essentially means you can write as fast as possible. The fastest way to write a block of data to the VDP is a series of sequential OUTI instructions (note: not OTIR), and an OUTI by itself takes 16 T-states.

So let's assume for simplicity's sake that you were doing nothing but writing consecutive bytes to the VDP using OUTI, and also let's assume you're using NTSC timings, which are more stringent than PAL (the frame is shorter):

The clock rate is about 3.58MHz and the frame rate 60Hz, so there are about 59,667 T-states in a frame, and around 227.7 T-states per line because there are 262 lines per frame.
Because active display is 192 lines that means active display is 43,725 T-states, leaving 15,941 T-states outside active display.

Outside active display then you can theoretically do around 996 OUTI instructions; and in active display the upper limit of what you can write to the VDP is something like 1507 bytes.
Adding those up you get 2503 bytes, which is enough to write the entire visible screenmap with plenty of change; or to "stream" around 78 tiles.

Now, those are theoretical maximums. You're going to have loads of other overheads that I won't go into here, and you're also not going to be able to write those bytes as efficiently as you want to, particularly in active display when the writes need to be throttled.

I seem to remember someone (@ichigo?) saying their rule of thumb for streaming tiles was something like 40 (i.e. 1280 bytes) per frame although someone else can probably provide a more accurate number.

Writing to VRAM during the active display will likely result in tearing and other visual artefacts if you're trying to update something which is actually onscreen, which may be the odd flickering you're referring to, although it's hard to know from what you describe.

Emulicious has an "event viewer" mode which is absolutely invaluable for figuring out exactly where in the frame cycle you are writing bytes to the VDP. The simplest is to try and keep all the VDP writes confined to the 70 lines of vblank/border region and do nothing during the active display. With the event viewer you can see this kind of thing at a glance.

EDIT:
the60ftatomicman wrote
(Why?) I want to have more than 8 characters on a line so this seemed like an easy way to do it.

Do you mean you want to animate things that look a little like sprites but using background tiles instead?
In this case, and assuming you are okay being constrained to an 8x8 grid, you could either stream the tiles themselves or prepare the pre-animated cels as separate tiles and modify the tilemap. The latter is obviously much quicker, but uses more tile definitions.
  View user's profile Send private message Visit poster's website
  • Joined: 30 Apr 2024
  • Posts: 8
Reply with quote
Post Posted: Thu May 09, 2024 8:16 pm
Thank you for detailed Reply!

I did mean updating the tilemap (not the tile data itself; sorry!) and in this case I am just doing timing testing and pushing a single hardcoded value TO VRAM
I wanted to see if it was even feasible as a concept so I am slowly introducing more and more as I go. I'm dangerous enough to make pixels appear but not fully understand what I am doing so let that add to this.

Here's the loop.


[code]
Load_Level_Ram_to_VRAM:
ld de,LevelPointerLocation
ld bc,(LevelRows*LevelColumns*2)-1
-:
ld a,(de)
ld h,a
inc de
ld a,(de)
ld l,a
call VRAM_Prepare
; -- do tile fetch here
push hl
ld hl,($C201)
ld a,$85 ; -- single tile for testing
pop hl
out (VDPDataPort),a
ld a,0 ; -- do tile flags (flip and such) here
out (VDPDataPort),a
inc hl
dec bc
ld a,c
or b
jr nz,-
ret
[code]
  View user's profile Send private message Visit poster's website
  • Joined: 30 Apr 2024
  • Posts: 8
Reply with quote
Post Posted: Thu May 09, 2024 8:31 pm
SOLVED MY PROBLEM!
In my main loop I was running ei before my halt command in each loop. silly mistake!
  View user's profile Send private message Visit poster's website
  • Joined: 06 Mar 2022
  • Posts: 683
  • Location: London, UK
Reply with quote
Post Posted: Thu May 09, 2024 9:54 pm
Glad you got it sorted. Without seeing the whole thing I can't say for sure, but often I would say an EI before waiting for an interrupt (e.g. with HALT) is probably what you would want to do, but oh well!

I hope you don't mind, but I had a look at the code you posted and thought it might be helpful to show how you might approach writing it a bit more compactly, using a couple of OUTI instructions in a loop. This isn't the fastest, but it's fast-er because the OUTI handles the incrementing of the source pointer as well as an 8-bit counter automatically for you, plus it's less complicated and shorter, once you take out all my comments.

I haven't tested this, and it's been a few weeks since I last wrote any assembler so might be bugs in it!

Also, see what I wrote about your ld bc,(LevelRows*LevelColumns*2)-1 line — it looks kind of dodgy to me, and may have been responsible for visual artefacts if it was pushing your copy routine across multiple frames.
EDIT: ah, perhaps it's just that I'm misread it – I think the -1 after the parentheses mean it's not indirect mode. Still, using parentheses in ld instructions is a little fraught and I'd advise against it ideally. It's rare to need to subtract 1 in a counter loop like that. Usually a decrement at the end of a loop gives you 0 at the right point without needing to do that.


Load_Level_Ram_to_VRAM:
  ; I assume VRAM_Prepare writes to the command port to set the initial VRAM address?
  ; if so, you probably only need to do this once, because the VDP auto-increments the
  ; address for you
  call VRAM_Prepare

  ; set up OUTI port - vdp data
  ld c, VDPDataPort
  ; you can load your RAM pointer directly into HL if you use OUTI
  ld hl, LevelPointerLocation
  ; your code used an indirect mode to load BC which might be a bug?
  ; also here we will use DE temporarily instead of BC (see below)
  ld de, LevelRows * LevelColumns * 2
  ; OUTI uses register B to count down and register C to hold the port,
  ; so we need to move the low byte of DE to B and the high byte
  ; in D we'll need to manually decrement inside the loop.
  ld b, e
  ; now we need to pre-adjust the high byte in case the low byte is zero
  ; using xor to clear register A is quicker than ld a, 0
  xor a
  ; test the low byte
  or b
  ; if it's not zero, increment the high byte
  jr z, @loop
  inc d

@loop:
  ; write two bytes from RAM to VDP (* see note below)
  ; each of these increments HL and decrements B
  ; the flags are set based on what's left in B after the decrement
  outi
  outi

  ; loop if low counter byte (register B) is not zero
  jr nz, @loop

  ; decrement the high byte (register D)
  dec d
  ; if it's not zero, loop all over again (i.e. 256 more times)
  jr nz, @loop
  ret

; * (note from above) this assumes that your tilemap data is all uncompressed in RAM
;   which I assumed because your count was LevelRows * LevelColumns * 2
;   if you are actually only storing the first byte, and want to use
;   a fixed value for the tile attributes (e.g. 0) then it's a little
;   more complicated, and instead of two OUTI instructions you might do:
;
  ; write tile index byte
  outi
  ; write fixed attribute byte (here = 0)
  xor a
  out (VDPDataPort), a
  ; restore the flags from register B so that the jr nz still works
  ; note: this relies on register A still being zero!
  or b
  ; if you wanted to be even more efficient and the attributes
  ; were always the same, you could set register E to hold the attributes before the loop
  ; and use out (c), e – then you wouldn't clobber the flags at all
  ; and wouldn't need the or b


Note I used a nested label @loop here for some clarity, but could also have used WLA's double underscore __ un-named label which supports forward and backward jumps with _f and _b respectively. See here.
  View user's profile Send private message Visit poster's website
  • Joined: 30 Apr 2024
  • Posts: 8
Reply with quote
Post Posted: Fri May 10, 2024 2:23 am
I take 0 offense! I’ll extend a thanks for not only reading but teaching 😊
  View user's profile Send private message Visit poster's website
Reply to topic



Back to the top of this page

Back to SMS Power!