Play Online
Preservation · Myths

Myths, tested

Common Derby Owners Club claims, checked against the actual arcade ROM. Each one gets the claim, the evidence, and a verdict. Evidence, not opinion: every offset is checkable in the real ROM.

The community trades a lot of lore about how Derby Owners Club works. Some of it is true, some of it is half remembered, and some of it is just wrong. This page settles the arguments by going to the source: the game's own program ROM, disassembled and read line by line.

Each myth below has its own shareable link. Click the 🔗 next to any title, then copy the address bar to drop that exact myth into a Discord or Facebook thread.

The myths

More to come. Got one you want tested? See the bottom of the page.

Myth #1

Turf can never be "Good to Soft"

The Claim

"Good to Soft is a dirt-only rating. In Derby Owners Club a turf race can never show Good to Soft. If you see Good to Soft, the surface has to be dirt."

Where it shows up: community Discord and Facebook debates about how the game decides the going.

The Evidence

Derby Owners Club stores the track condition as a single number from 0 to 3, on one going scale, and it is the same scale for every race:

ValueTrack condition
0Good
1Good to Soft
2Soft
3Heavy

That number is generated from the weather and race data, then printed by the on-screen TRACK CONDITION line. Whether the race runs on turf or dirt is stored separately, as part of the race identity; the race name itself carries TURF or DIRT. The code that sets the track condition never looks at the surface.

Four routines in the program ROM write the track condition. Every one of them sets it from weather or race data, and not one of them reads the surface:

Routine (ROM address)How it sets the conditionReads surface?
0x03E778 / 0x03EA32High nibble of a packed race-data byteNo
0x03F956Byte copied straight from the race recordNo
0x054012Lookup in the weather-to-going tableNo

The clincher: the game's own weather-to-going table emits the Good to Soft value directly. Its outputs across all 16 entries are exactly the set {Good, Good to Soft, Soft, Heavy}, and the Good to Soft value appears at two entries:

Table entryTrack condition produced
2Soft
4Good to Soft
7Soft
10Good to Soft
14Heavy
all othersGood

The input to that table is the weather and the race seed. There is no surface input anywhere in the chain. When the weather lands on those entries, the going becomes Good to Soft for the race, turf or dirt alike.

Show the full ROM proof (offsets and disassembly)

All addresses below are reproducible in the standard drbyocwc ROM set: World Edition Rev C program ROM epr-22336c.ic22, CRC32 50053F82. It is a Sega Naomi SH-4 title; the ROM image loads at 0x0C020000, so file offsets and RAM addresses line up directly.

The condition value and its string table. The condition is one global at 0x0C21A2B4 (race-info struct base 0x0C21A294, field +0x20). It indexes a four-pointer string table at file 0x150BD0:

value 0 -> "GOOD"          (file 0x10EB3C)
value 1 -> "GOOD TO SOFT"  (file 0x10EB44)
value 2 -> "SOFT"          (file 0x10EB54)
value 3 -> "HEAVY"         (file 0x10EB5C)

The renderer at RAM 0x0C076B3A reads the value raw and indexes that table. No mask, no surface read:

0x0C076B3A  mov.l  0xc076be4,r3   ; r3 = &condition (0x0C21A2B4)
0x0C076B3C  mov.l  0xc076be8,r0   ; r0 = value table (0x0C170BD0)
0x0C076B3E  mov.l  @r3,r2         ; r2 = condition (0..3), read raw
0x0C076B40  mov.l  0xc076bec,r1   ; r1 = "TRACK CONDITION %s"
0x0C076B42  shll2  r2             ; r2 = condition * 4
0x0C076B46  mov.l  @(r0,r2),r2    ; r2 = table[condition]  (no surface input)
0x0C076B4A  jsr    @r3            ; sprintf

The four writers that set the condition, with the value each one stores:

0x03E778 / 0x03EA32 : condition = (raceData[+0x28] >> 4) & 0x0F
0x03F956            : condition = byte at raceRecord[+0x72]
0x054012            : condition = weatherGoingTable[(seed >> 3) & 0x0F]

None of them loads the surface field or branches on turf versus dirt.

The weather-to-going table at file 0x10FCA4. The going column across all 16 entries is the set {0,1,2,3}; value 1 (Good to Soft) sits at entries 4 and 10. The seed comes from the weather and RNG routine at 0x0C0B1E60:

entry  0 -> Good           entry  8 -> Good
entry  1 -> Good           entry  9 -> Good
entry  2 -> Soft           entry 10 -> Good to Soft
entry  3 -> Good           entry 11 -> Good
entry  4 -> Good to Soft   entry 12 -> Good
entry  5 -> Good           entry 13 -> Good
entry  6 -> Good           entry 14 -> Heavy
entry  7 -> Soft           entry 15 -> Good
distinct outputs = { Good, Good to Soft, Soft, Heavy }

The surface lives in the race identity: the race-name table at file 0x0C6940 carries TURF or DIRT per race. None of the four condition writers read it. Surface and going are independent fields.

Verdict: False

Turf can be Good to Soft. There is no turf exclusion anywhere in the game.

Good to Soft is generated independently of the surface. The game's own going table produces it, and the routine that prints the condition never checks turf or dirt. Turf and dirt draw from the exact same mechanism, so a turf race can absolutely be Good to Soft.

Verify it yourself: open epr-22336c.ic22 (CRC32 50053F82) from the standard drbyocwc ROM set and check the offsets in the proof above.

Footnote: Rev D is different

Everything above is the World Edition Rev C build (the common drbyocwc set). The later Rev D build (World Edition EX, derbyocw) quietly rebuilt the going system, and on Rev D the answer flips: the going IS tied to the surface, and "Good to Soft" was removed from the game entirely.

Rev D uses two separate going vocabularies, one for dirt and one for turf:

Dirt goingTurf going
FastFirm
GoodGood
HeavySoft
MuddyYielding

So on Rev D a turf race can show Firm, Good, Soft, or Yielding, and a dirt race can show Fast, Good, Heavy, or Muddy. There is no "Good to Soft" rating on Rev D at all. The bottom line is that this is version dependent: on Rev C the going is one shared scale and a turf race can be Good to Soft, while on Rev D the going words follow the surface. That Rev D behavior is most likely where the "Good to Soft is a dirt-only rating" belief comes from.

Show the Rev D ROM proof

Rev D program ROM epr-22336d.ic22, CRC32 E6C0CB0C (the derbyocw set). The on-screen condition words are drawn from one string table that is split into a dirt half and a turf half:

condition string table @0x0C173208 (8 going entries):
  index 0  FAST      index 4  FIRM
  index 1  GOOD      index 5  GOOD
  index 2  HEAVY     index 6  SOFT
  index 3  MUDDY     index 7  YIELDING
indices 0-3 = the dirt going set, 4-7 = the turf going set
renderer at 0x0C075676 indexes this table by the going value

The surface-only words make the split unambiguous: Fast and Muddy (dirt terms) sit in the lower half at 0 and 3, while Firm and Yielding (turf terms) sit in the upper half at 4 and 7. Rev C, by contrast, has a single four-word table (Good, Good to Soft, Soft, Heavy) with no surface input at all. The phrase "GOOD TO SOFT" exists in the Rev C ROM and is absent from the Rev D ROM.

Myth #2

Beer is a food, feed it for a boost

The Claim

"Draft Beer and Black Draft Beer are real foods in Derby Owners Club. Feed your horse beer and it works like any other treat: it raises its stats."

Where it shows up: stable and breeding chat, where beer splits the room. One camp says it is just a gag with no function, the other swears it boosts the horse.

The Evidence

Both halves of the argument are half right, so we tested each one against the ROM. The food list holds 45 items, and beer is two of them:

Food IDNameRecordGrowth payload
0Carrotnormal 44-byte recordhas stat points
36 / 37Korean Ginseng / Largenormal record2,2,2,2,2,2 doubles to 4,4,4,4,4,4
43Draft Beernormal record0,0,0,0,0,0
44Black Draft Beernormal record0,0,0,0,0,0

Beer is a genuine food, not a gag. Each food is a 44-byte record with a name, an artwork pointer, and a block of stat values. Draft Beer and Black Draft Beer are complete, well-formed records, and the routine that looks a food up by its number is plain arithmetic with no "is this a real food" check. So the game reaches beer exactly the way it reaches a carrot.

But beer boosts nothing. The six stat-growth bytes in every food record are what a feed actually adds to the horse. We know these six bytes are the growth values because the "Large" version of any food carries the same six numbers scaled up: Korean Ginseng is 2,2,2,2,2,2 and Large Korean Ginseng is exactly 4,4,4,4,4,4. Beer's six bytes are all zero. It is the only food in the game with an all-zero payload. Feeding beer adds nothing to any stat.

Instead, beer is special-cased for a reaction. The stable code singles out beer by ID in three places (grouped with bananas as novelty items). Rather than running the normal feed-and-grow path, beer sets a small reaction state (Draft Beer and Black Draft Beer each flag a different one) and pulls from its own dedicated table of sequence pointers. In other words the developers wrote special code so beer does something on screen, just not a stat gain.

So is beer actually offered to you in this version? That is a separate system from the stat values, and it is where the trail goes cold in the ROM. Whether a food shows up in your feed choices is decided by a small runtime table the game builds when a session starts: the offer routine reads that table per food and offers the food when its value clears a threshold. We traced that table all the way back to the code that fills it, and found two things. First, there is no beer-specific "off" switch anywhere: not in beer's record, not in the lookup, not in the availability data, and not in the table's setup code. Beer is handled by the same general machinery as every other food. Second, the table is not a fixed list in the ROM; the game computes it at runtime. That means the one remaining question, does beer's entry land in the offerable range in this build, cannot be answered by reading the ROM alone. It has to be read from the game while it runs.

Show the full ROM proof (offsets and disassembly)

All addresses are reproducible in the standard drbyocwc ROM set: World Edition Rev C program ROM epr-22336c.ic22, CRC32 50053F82. Sega Naomi SH-4; the image loads at 0x0C020000, so file offsets and RAM addresses line up.

The food table is at 0x0C186A7C, 45 entries of 44 bytes. Each record: name (24 bytes), artwork pointer (+0x18), then the stat-growth bytes at +0x1C through +0x21. The lookup at 0x0C09AD3E is ungated arithmetic, so any ID resolves:

0x0C09AD3E  mov   #44,r0       ; record stride
0x0C09AD44  muls.w r0,r4        ; id * 44
0x0C09AD4A  add   r3,r0         ; + table base, no range check
0x0C09AD4C  rts

The six growth bytes are the effect. Read across all 45 records, the +0x1C..+0x21 bytes are what differs by food, and the Large variants are the small ones scaled up:

id 36 KOREAN GINSENG        1C..21 = 02 02 02 02 02 02
id 37 LARGE KOREAN GINSENG  1C..21 = 04 04 04 04 04 04   ; exact double
id  0 CARROT                1C..21 = 02 00 00 00 00 00
id 43 DRAFT BEER            1C..21 = 00 00 00 00 00 00   ; all zero
id 44 BLACK DRAFT BEER      1C..21 = 00 00 00 00 00 00   ; all zero

Beer is special-cased by ID at three stable-code sites (each tests both 43 and 44, with no food-count bound, so these are beer-only branches):

0x0C0A4110  draw path  : beer uses hardcoded display offsets
                         (0x764 / 0x790) into a separate buffer,
                         not the normal table + id*44 indexing
0x0C0A5B44  feed path  : bananas (41/42) + beer (43/44) share a
                         branch; beer writes a reaction state at
                         0x0C21A4F4 (draft=1, black draft=2)
0x0C0A5FA0  feed path  : beer indexes its own pointer tables at
                         0x0C187E60 / 0x0C187E8C (its reaction
                         sequence), bypassing normal handling

The feed routine never reads the growth bytes for beer; it runs the reaction path above. Normal foods fall through to the usual grow path.

The offer gate (the access question). Whether a food is offered is decided here, independently of the growth bytes:

0x0C04806C  offer builder : for food 0..44, read the runtime
                          array at 0x0C21D738 + food*4, look it up
                          in the availability table, and offer the
                          food when the result is < 3
0x0C09AD50  lookup        : reads AVAIL[value*16 + mode_column]
0x0C1467FC  AVAIL table   : 16 bytes per row; beer's rows are
                          normal and offerable, not gated

Who fills the runtime array (this was the wall). The array at 0x0C21D738 is care-state base 0x0C21D6B8 + 0x80. It is filled by the care-state init:

0x0C03563C  init fill loop : writes 0x0C21D738[index] for the
                           first 40 foods; each entry is the
                           return of a runtime divide routine
                           (0x0C0C211C), not a value copied from
                           a fixed table in the ROM
beer entries  : DRAFT BEER       -> 0x0C21D7E4  (0x0C21D738 + 43*4)
                BLACK DRAFT BEER  -> 0x0C21D7E8  (0x0C21D738 + 44*4)

Because the values are computed live, the ROM does not state beer's final offer value. Reading those two RAM addresses while the game runs is what closes it.

Verdict: Open, needs more analysis

Beer is a real, fully coded food with no stat effect. Whether it is actually served to you in this build is still open, and you can help close it.

Here is what the ROM settles for certain. Beer is not a gag: Draft Beer and Black Draft Beer are complete, normal food records, and the game even writes special code just for them. And beer boosts nothing: its six growth values are all zero, the only food in the game like that. There is also no beer-specific "off" switch anywhere we looked.

What is still open is whether beer actually appears in your feed choices in World Edition Rev C. The game builds that decision from a small table it computes while running, not from a fixed list in the ROM, so reading the cartridge alone cannot answer it. This is where a community hand would finish the job:

  • Easiest, just play. Across different game modes and regions, watch whether Draft Beer or Black Draft Beer ever show up as a food you can pick. Note when and where they do or do not appear, and share it.
  • Read it live in Flycast. While a raising session is running, read NAOMI system RAM at 0x0C21D7E4 (Draft Beer) and 0x0C21D7E8 (Black Draft Beer). A value under 3 means the game considers that beer offerable in the current mode.
  • Finish the ROM side. The per-food offer values come out of a divide routine at 0x0C0C211C filled from 0x0C03563C. Working out its inputs would give a static answer for beer without needing a live read.
  • Name the six stats. The six growth bytes at record offset +0x1C..+0x21 are applied to the horse by the training code. Mapping each byte to an on-screen stat (Speed, Stamina, and so on) would tell us exactly what beer gives zero of.

Everything above is reproducible from epr-22336c.ic22 (CRC32 50053F82). If you take any of these further, bring it to the Discord and it will be folded in here with credit.

Myth #3

The Competing stat does nothing, and that is what changed in Rev D

The Claim

"The Competing stat doesn't matter. That is what makes Rev D different from Rev C."

Where it shows up: passed around the community by a long-time player, as advice on where not to spend your training.

The Evidence

This claim has two halves, and they do not fail the same way. One of them is very nearly right. The other is wrong, and the real Rev D difference turns out to be something else entirely.

Half one: is Competing worth investing in?

We built four horses that were identical in every respect the game tracks: same sire and dam, same race record, same earnings, same three internal stats, same running style, and the same total externals. The only difference was which external carried the high value. Each horse loaded one stat to 63 and left the other three at 32.

Before the gates opened, the game ranked them. This ranking is computed from the horse's stats, so it is the game telling you what it thinks each stat is worth:

FavoriteHorseStat loaded to 63
1st favoriteQOOBOut of the Box
2nd favoriteQSPRTSpurt
3rd favoriteQTENTenacious
4th favoriteQCOMPCompeting

Same points, same cost, and the Competing horse is rated last. Then they raced, over 2000m on turf:

FinishHorseStat loadedTime
1stQSPRTSpurt2.02.57
3rdQOOBOut of the Box2.02.57
4thQTENTenacious2.02.57
5thQCOMPCompeting2.02.67

Three of the four ran the identical time. The Competing horse was the only one that could not keep up, and it was beaten by a computer horse in the process. The game's own ranking and the actual race agree with each other, and both put Competing last.

To be precise about what "doesn't matter" can and cannot mean: the stat is not dead. We disassembled both program ROMs, and Competing is read and processed on exactly the same footing as the other externals: copied three ways into each race entity, run through its own conversion routine, and consulted by the game's own horse-evaluation ladder. Nothing skips it or zeroes it. But among the five externals that get a conversion constant, Competing's is the smallest, a twentieth of the one Tenacious gets. We have not yet proven how those converted values feed race speed, so treat that as a hint rather than a measurement. The measurements are the tables above.

Full honesty on sample size: we also ran head-to-head rounds between horses differing only in Competing (1 vs 21 vs 42 vs 63), and those were mixed. One round produced a perfect finishing gradient in Competing order, another leaned the other way. The four-way ranking round above is the cleanest single result so far, and more rounds are queued. What no result has shown is Competing outperforming an equal investment in any other external.

There is a second, quieter cost. Derby Owners Club damps your training gains as your six externals add up. The higher your total, the less each session gives you, while a failed training still takes the full amount away. In our tests, horses with a total around 227 to 245 gained from rest and training, while horses at 263 to 281 gained nothing visible and still lost 3 points on a failure. So points poured into a weak stat are not merely wasted. They also throttle everything you try to add afterwards.

Half two: is this what changed in Rev D?

No. Competing is handled identically in both revisions. The routine that converts it is the same nine instructions, using the same constant, in Rev C and Rev D alike. Nothing about this stat was touched.

What actually changed in Rev D is a ceiling on internal growth. Once a horse's three internal stats add up to more than 160, the ordinary per-race growth switches off. Decay from neglect still runs, so a Rev D horse settles just above 160 and hovers there: a ceiling it drifts back to, not a cliff it falls off. Rev D also lowered the per-stat ceiling from 60 to 55. Rev C already slowed growth at higher totals, so this was a re-tune that added a lower, harder rung rather than a brand new idea, and Rev D even loosened one veteran career band (35 races where Rev C used 30). Externals keep improving as normal throughout, which is why the change is easy to misread: your horse is still developing, just never where you are looking.

We tested this with two horses that were identical apart from their internals, on the same cabinet, on the same evening:

HorseInternals totalResult in Rev D
Willy JR169, above the capNo internal growth at all across 4 races, including a Grade 1 win
Test twin120, below the capGrew on his very first race

One number changed between them, and the outcome flipped. The same horse on Rev C had gained on every single race.

Verdict: Partially explained

Right about the stat. Wrong about the revision.

Competing is a real, processed stat, so "does nothing" is too strong. What we can say is narrow and specific to Competing: on the cabinet, among horses that cost the same, the game ranked it last and the race agreed, its conversion constant is the smallest of the five, and stacking it damps your future training on top. As practical advice, "do not pour points into Competing" holds up.

What we deliberately do not claim is a general ranking of the six externals. Our own verification found that an apparent ordering came back out of the simulator's own weighting constant, which means it was measuring its assumptions rather than the game. Until that is settled against the ROM, treat "which external is best to train" as unanswered.

But it is not the Rev C to Rev D difference. Competing is byte for byte identical between the two. What Rev D did change is a growth ceiling: once your three internals pass 160 the ordinary growth switches off and the horse settles there, and the per-stat ceiling drops from 60 to 55.

That is a partial answer, not a solved myth. The ceiling is real and we can point at the instructions that do it, but it does not account for everything players describe. It says nothing about why a stronger horse would feel slower within a single race: at any given moment a higher total still scores higher in both revisions. And because Rev D also caps each stat at 55, the highest reachable total is 165, so 160 sits right against the ceiling. A horse bred at 150 needs roughly eleven races to get there, which is too slow to explain a difference people notice early. Something else is still undiscovered, and we would rather leave this open than close it neatly.

Tested with purpose-built horses on a multi-board rig, alongside a full disassembly of both program ROMs, independently re-derived by a second pass. Findings on the stat ranking come from one controlled round and will be updated as we run more.

Have a myth to test?

Heard a claim about Derby Owners Club and want it settled from the ROM? Bring it to the community and it may end up here, tested and answered.

Join the Discord → The preservation story