So all data coming from the roms is inverted. And different bit patterns will do different items:
Code
65 32 0 is a slick pattern 01 (24) not 2 4 reversed = not 4 2
65432 0 is finish line pattern 00 (24) not 2 not 4 reversed = 4 2
6543 0 is checkpoint 65 is checkpoint 1
6 43 0 is checkpoint 6 is checkpoint 2
543 0 is checkpoint 5 is checkpoint 3
43 0 is checkpoint is checkpoint 4
checkpoint is pattern 10 (24) 2 not 4 reversed = 4 not 2 and 65 determine checkpoint number
3 0 is score pattern 11 (24) 2 4 reversed is not 4 not 2
=========================
0 3 not 1 is special data (that is one of the finish,slick,checkpoint or score)
Bits 0 1 and 3 determine special data:
bits 2 and 4 get put into a 9321 decoder to determine the special item to display
One of the things that's been puzzling is that one page of the schematics in the Lemans manual is missing. There's 2 pages of sheets 1 and 2 but half of sheet 3 is missing.
Fortunately, the Gran Trak 10 manual has a very similar schematic that can fill in the missing page.
(The schematic says its size D so half must be missing)
Doing some research on Gran Trak 10 I came across this great write up on fixing a Gran Trak 10 by Ed Fries. It's a really fun read.
In case this comes up AGAIN, here is the preliminary datasheet for the Atari Le Mans PROM.
It's been identified through past Internet research as a Mostek MK28000 PROM (the same as in Tank / Tank II.) 313K mk28000p.pdf - http://www.sendspace.com/file/36nkqm
Sometimes you can't figure out how things work unless you build a model. I couldn't quite see how the horizontal counter system worked in Lemans.
The manual lays it out, but that seems confusing.
So let's take the schematic and make a logisim model:
and what you see is that 512H isn't actually 512H but represents more of the "right half of the screen" since it gets set after the 0x1c2 clock and the counter gets reset but leaving the 512H bit making 0x200. (In other words, the counter jumps from 0x1c2 to 0x200). Once it hits 0x3c2 then it gets reset again back to 0x000, and then does HSYNC for 32 clocks.
This explains why the 7th row byte disappears in the racetrack. 512/64 = 8 for 8 bytes, but 0x1c2 / 64 = 7.03 so it jumps from 0x1c2 to 0x200 which skips that seventh byte.
Studying the ROM addressing is kinda overwhelming at first, but they've given a nice table in the manual to help out.
There's 6 groups given:
CAR = AB SCORE = C TIME = D RACETRACK1 = E RACETRACK2 = F SPEED CODE = G
(I think B is supposed to be for a dual player version like Gran Trak 20, you see B mentioned on the schematics) (E and F are pretty much the same thing, just differing by the TSA track select A)
Theres a bunch of 74153 multiplexers to choose the various inputs specific to each group. The lower 3 74153s handle A0 A1 A2 A3 and A5 A6. The upper 74153 does A7 and A8.
The lower group has ABCD coming in on select 1 and CDEF coming in on select 2.
If the bit on a select is 0 then choose the group items that aren't part of it (ie invert the set) total set = ABCDEFG so select1 = ABCD inverted select 1 is EFG and select 2 = CDEF inverted select 2 = ABG
sel2 sel 1 11 = CD (select 1 = 1 ABCD select 2 = 1 CDEF, interesection is CD) 10 = EF (select 1 = 0 EFG select 2 = 1 CDEF, intersection is EF) 01 = AB (select1 = 1 ABCD select 2 = 0 ABG, intersection is AB) 00 = G (select1=0 EFG and select 2 = 0 ABG, intersection is G)
===================
The upper 74153 is a little different
select 1 is depends on 512H and also on LD1B (AB). This has the effect of displaying the score on the left half of the screen and the time on the right half. select 2 is not SCOREDISP (since SCOREDISP is basically CD) so select 2 is not CD = select 2 is ABEFG
11 = EFG (not LD1B = not AB = CDEFG, intersecting with ABEFG, result is EFG) 10 = AB (LD1B = AB intersecting with ABEFG, result is AB) 01 = D time (CD choosing either C or D depending on 512H) 00 = C score
The speed code is interesting because the speed data resides in the first 0x21 bytes of the track. byte 0 to 0x1f are speed codes for the 31 different directions and byte 0x20 is the stop code. When the car has a "stop condition" 1STOP = 1 and address becomes 0x20 because the output strobe to the 74153 are shut off, so A0 to A4 is 0, while A5 is set to 1.
I think that the speed data is accessed during the VSYNC so the address bits A6 to A8 should be zero. There's no separate logic to gate off A6-A8 for speed code G since those bits 64V, 128V and 256V are expected to be zero during the VSYNC.
======================
so I think what happens is that on VSYNC, the horizontal and vertical motion counters get loaded with rom data from the SPEED G address.
on each HSYNC, the graphics for the car get loaded from the CAR A Address.
As the screen scans, the horizontal counters load the track data from the RACETRACK EF address.
Special codes in the racetrack data do things like SLICK (which requires no data) and SCOREDISP (which will read 8 bits to display from the SCORE C or TIME D address at the next fetch opportunity, so it actually displays in the next track byte after the code appears in the track)
The other special codes are CHECKPOINT 1-4 and FINISH LINE.
Well, the vertical position counter is 12 bits (0-4096) and if we reset it to zero at the start of the frame, where will the car end up?
The screen is 521 lines so let's divide 4096 by 521.
so we divide it and we get 7.86 frames
> (4096 / 521) 7.8618042226488
multiply 7 * 521 = 3647
and subtract 4096 - 3647 = 449 which is 0x1c1 which is really close to 0x1b0. Maybe it's slightly different because of when the vertical blank hits.
===============================
Also I was trying to figure out how the 9314 worked (used to do checkpoints and scoring) and recreate it in Logisim, but it's got this weird symbol that looks like an AND gate but the inputs come into the side.
What does this mean?
edit: after a bunch of searching around, it's a wired and.
One of the things that had me puzzled was this circuit for the extended play. If you score a certain amount, you get a free game, but only one free game per credit.
so hitting free game signal (FGS) will cause both NAND SR latches to flip
but it doesn't look like anything actually changed because NOT EXTENDED PLAY seems to remain 1.
So I built the circuit in Lushprojects circuitjs and it becomes easy to see, when you hit FGS, it causes NOT EXTENDED PLAY to go momentarily to 0, then that 0 feeds back to the 7410 input, flipping the SR latch making the topmost input to the second stage 7410 a zero which makes NOT EXTENDED PLAY go back to 1.
If you look at the scope on the bottom, you can see the voltage drop down momentarily (the green line) to make a logic 0 and then go right back up. It took me awhile to figure out how to add a scope to the bottom pane, add Voltmeter/Scope Probe, then right click on it and View in New Scope.
Circuitjs saves its circuits as text so you can select "Import from Text" and paste this in:
I could see that nld_tms4800 rom wasn't outputting any data, so after fixing that
Code
else
{
// unsigned d = 0x00; // doesn't actually output any data setting d to zero
unsigned d = m_last_data;
for (std::size_t i=0; i<4; i++)
{
if (m_OE1())
m_D[i].push((d >> i) & 1, delay);
if (m_OE2())
m_D[i+4].push((d >> (i+4)) & 1, delay);
}
}
}
It looks a little bit recognizable. I'm really confused by fixfreq.cpp and how it handles the sync so I see about one frame of screen then nothing.
Other weird things is that the 7493 at F2 seems to pick up spurious values, it should start at zero (which is does) but then immediately jumps to a value of 3? It's supposed to be clocked by HBLANK, but that seems to fire very early when it should not.
Code
cnt = 0 time = 0.000000 time = 0.000000 data= 0.470887
UPDATE_id_1v TIME: 0.000000 id_1v= 3.999999 f293_out=1
UPDATE_id_2v TIME: 0.000000 id_2v= 3.999999 f293_out=3 // why is this getting a value of 3?
UPDATE id_16v: 0.000000 id_16v= 3.999999 // the other 93 at H2 is doing the exact same thing
UPDATE id_32v: 0.000000 id_32v= 3.999999 // the other 93 at H2 is doing the exact same thing getting a value of 3?
TMS4800 ROM ADDRESS 0 = 4f 6 3210
UPDATE abg: 0.000000 abg= 3.999999
UPDATE TEST2: 0.000000 data= 3.999999
UPDATE data7: 0.000000 data7= 3.999999
UPDATE data6: 0.000000 data6= 3.999999
UPDATE data5: 0.000000 data5= 3.999999
UPDATE data4: 0.000000 data4= 3.999999
UPDATE data3: 0.000000 data3= 3.999999
UPDATE data2: 0.000000 data2= 3.999999
UPDATE data1: 0.000000 data1= 3.999999
UPDATE data0: 0.000000 data0= 3.999999
UPDATE a4: 0.000000 a4= 3.999999
UPDATE a9: 0.000000 a9= 3.999999
UPDATE a10: 0.000000 a10= 3.999999
UPDATE abcd: 0.000000 abcd= 3.999999
UPDATE ld1b: 0.000000 ld1b= 3.999999
UPDATE vsync: 0.000000 vsync= 3.999999
UPDATE hblank: 0.000000 hblank= 3.999999 // hblank on?
TMS4800 ROM ADDRESS 400 = 0
TMS4800 ROM ADDRESS 600 = 1 0
TMS4800 ROM ADDRESS 610 = 80 7
UPDATE vld1: 0.000000 vld1= 3.999999
UPDATE hblank: 0.000000 hblank= 0.100000 // now hblank off?
UPDATE vsync: 0.000000 vsync= 0.100000
UPDATE a4: 0.000000 a4= 0.100000
TMS4800 ROM ADDRESS 600 = 1 0
UPDATE abg: 0.000000 abg= 0.100000
UPDATE_id_1v TIME: 0.000000 id_1v= 0.100000 f293_out=2
UPDATE TEST: 0.000000 data= 0.103274
cnt = 1 time = 0.000000 time = 0.000000 data= 1.183559
UPDATE hblank: 0.000000 hblank= 3.999999 // now hblank on?
UPDATE a4: 0.000000 a4= 3.999999
UPDATE TEST: 0.000000 data= 3.435158
Looking at JD's Tank driver, it uses the PROM_MK28000 instead of the TMS4800. Trying that since it may have different timing and I get this:
As much as I'd love to, I can't really help you here, as it was long enough ago that I have no idea why what I implemented for Tank seems to work better.
It's all good, JD. I'm just happy to have something that looks sane 8-)
I think I may have an answer for why the 7493 ends up with a value of 0x3, when it first updates A and B, the current code does an increment without even looking at the state of the clkA or clkB input, just increments both m_a and m_bcd "blindly".
After going back to the master version of fixfreq the screen seems to be syncing now.
The car is displaying but for some reason, it is jumping around horizontally and it seems to be skewed. However, it seems to remain on the same vertical position, which is good.
I tried everything I could think of to make the horizontal counters work for the car display, and it finally occurred to me that for some reason the horizontal count is off by a couple of numbers.
So hard wiring the horizontal counter load to be a 6 instead of an 8 so that it will count two extra before hitting full, I can get the car to display in one place. The car doesn't move when the amount the counter has to count up matches the horizontal size of the screen.
However, it seems to display outside the area of the track. I stuck in a counter and hooked up the directions to the counter outputs to make it spin.
It runs pretty slow, but I'm generating tons of printfs.
edit: turned off all printfs and turned off the debugger and it runs pretty fast, though there's a lot of up and down screen movement.
Ok, I'm able to move the car a little. I say a little because it's hard to move it since you're clocking a flip flop. It expects to get pulses. But it will move 8-)
Since the horizontal counter seemed to be two off, I changed the main horizontal counter to use the 1H instead of 2H when it computes the end of the line and it seems to work ok. (The first time I tried it it didn't work, then I remembered that I should change the horizontal counter load back to 8 instead of 6 to match).
I hacked a 74193 counter to drive the 1D1 .. 1D4 and 1R0..1R3 outputs and I can turn the car.
There's some real strangeness with the timing and Netlist.
I thought I'd clean up my code and all of a sudden the horizontal counter wasn't working:
Why would swapping
Code
TTL_7408_AND(HACK, 1H, P) // see if changing it to 1H fixes the car
TTL_7408_AND(E2_A, HACK.Q, 64H) // see if changing it to 1H fixes the car
with
Code
TTL_7408_AND(E2_A, 1H, 64H)
make it so the horizontal counter would have different behavior?
Code
#ifdef HORIZ_COUNTER_ADJ
#pragma message "USING 1H CLOCK " HORIZ_COUNTER_ADJ
// TTL_7408_AND(E2_A, 1H, 64H)
TTL_7408_AND(HACK, 1H, P) // see if changing it to 1H fixes the car
TTL_7408_AND(E2_A, HACK.Q, 64H) // see if changing it to 1H fixes the car
#else
TTL_7408_AND(E2_A, 2H, 64H)
#endif
ALIAS(2H_AND_64H, E2_A.Q)
TTL_7410_NAND(J1_C, 2H_AND_64H, 128H, 256H) // 450th horizontal pixel (active low)
// 256 + 128 = 384 + 64 = 448 + 2 = 450
edit: I tried such things as using a 4 input NAND and an 8 input NAND but neither 1H or 2H work properly. I must've found some combination of timing that works.
// TTL_7420_NAND(J1_C, 1H, 64H, 128H, 256H) // neither 1H or 2H work // TTL_7430_NAND(J1_C, 1H, 64H, 128H, 256H, P, P, P, P) // neither 1H or 2H work
Guess I'm going to keep the ugliness if it works...
I managed to get the start button to work and it sets the counter to 78 but the score instantly jumps to 8 and the checkpoints aren't working yet, or the countdown. (edit: looking at the nld_dm9314.cpp it seems that RS mode is not yet implemented for the 9314. That would keep the checkpoints from working 8-)
Another strange thing I'm noticing is that the UI doesn't seem to respond properly.
With a slow driver like netlist, you can bring up the UI menus, but then hitting Enter (to enter Input Settings for example) won't do anything and the UI kinda freezes to keyboard input, but the mouse pointer menu activity is still active.
It's kind of intermittent thing, sometimes it works other times it doesn't.
Ok, modifying nld_dm9314.cpp to do some RS action:
Code
NETLIB_HANDLERI(inputs)
{
netlist_time delay = NLTIME_FROM_NS(24); //FIXME!
if (!m_MRQ())
{
/* Reset! */
for (std::size_t i=0; i<4; i++)
m_Q[i].push(0, delay);
} else {
for (std::size_t i=0; i<4; i++)
{
if (m_SQ[i]())
{
/* R-S Mode */
// FIXME: R-S mode is not yet implemented!
// RS mode is just an "extension of regular D mode"
// way RS mode works is that D and S bar should be high (keeps old value)
// S bar going low sets output high
// Data going low and S bar high sets output low
// S bar going low AND D going low sets output low (D takes precedence)
if (!m_EQ())
if (!m_D[i]()) // if D low and SQ high we clear the bit
{
m_last_Q &= ~(1 << i);
m_Q[i].push((m_last_Q & (1<<i))>>i, delay);
}
}
else // SQ is Low
{
/* D Mode */
if (!m_EQ())
{
m_Q[i].push(m_D[i](), delay);
m_last_Q &= ~(1 << i);
m_last_Q |= (m_D[i]() << i);
}
}
}
}
}
And also realizing that I swapped the D and S parameters when I use the 9314, (really easy to do... 8-)
now I can do a full lap with the checkpoint scoring working!
Looks like you're pretty much YOLO-ing your way into a discrete driver. I like it. Bit tired to be doing any code review today, but if there's anything that Vas hasn't covered about 12-16 hours from now, I'll have a look.
Since I'm on a roll, I added the 555 1second timer and it seems to work!
The driver is only running at 10% speed (on my system) so I made the resistance much lower (supposed to be 220K, set it to 20K otherwise I'd never see the end of the countdown).
Once it counts down, the car won't move forward (you can still turn it though).
So I looked at the schematic over and over again, trying different things and I've come to the conclusion that the RESET1 D-Latch must depend on the horizontal position somehow, either through HBLANK or 512H.
RESET1 pulse ends when it clocks in a P on the data line. If this happens when 512H goes high, the car will appear in the middle of the screen.
Code
TTL_7408_AND(Reset1Clock, VLd1_Q, 512H) // clock the reset1 flipflop depending on 512H
TTL_7474(A4_A, Reset1Clock.Q, P, A6_B.Q, P)
// TTL_7474(A4_A, VLd1_Q, P, A6_B.Q, P) // according to schematic
ALIAS(RESET1_Q, A4_A.Q)
ALIAS(RESET1, A4_A.QQ)
TTL_7400_NAND(A2_B, CAR1VIDEO, COMP_SYNC_Q)
TTL_7408_AND(A6_B, START_Q, A2_B.Q)
If you think about it awhile (it just came to me), no additional gating necessary.
You can set the flipflop with PRESET_Q so just connecting 512H_Q up will set it and shut off the RESET1.
Code
TTL_7474(A4_A, VLd1_Q, P, A6_B.Q, 512H_Q)
It's actually kind of interesting to study these early Atari arcade games like Lemans/Gran Trak 10 and Tank because you see the similarities between them and what came later in the 2600. The horizontal and vertical position and motion based on counters, here discrete logic does the counting for vertical and horizontal, whereas the 2600 had the cpu do the vertical counting, and the horizontal counting was done with a polynomial counter with an cpu supplied HMOVE offset.
Another exciting patent about composing complex graphics without framebuffer is US4383827 and DE3243574A1 about Nürburgring 3, the mythical arcade driving simulator by Reiner Foerst.
If you are emulating such things, a specimen has apparently survived in the Computer Games Museum of Berlin.
I am writing on a more detailed summary about the different Foerst hardware platforms. Nürburgring 1 & 2 were the predecessor of Atari "Night Driver" but had mostly analogue computers for car simulation, graphics and sound. Nürburgring 3 had more realistic colour graphics controlled by a Z80 MK3880 CPU but still used much analogue hardware for the simulator.
Foerst GmbH, Historie
According to patent DE3243574A1 the colour graphics works without framebuffer using gate logics with counters to draw the road etc., likely controlled by an eprom. Although this machine yet had no opponent cars, the graphics style of drawing a 3D road with center line and horizontally scrolling 2D backdrop (sky and mountains) had set the standard for games like Sega's "Turbo" (1981, which completely lacked car physics and switched curves as static still pictures) and the famous "Pole Position" (1982 by Namco, having car physics and proper curve motion).
Versions N4/N5 (1982,1983) were fully digital with improved arcade graphics featuring opposing traffic and better character generator. N6 (1986?) was likely for professional simulators only and had very awesome hardware based weather effects (apparently alpha channel) and hardware based area fill and circle segment drawing for a kind of zooming run-length encoded sprite graphics. N7 (1985, used in a serious forklift simulator) employed a custom built highend parallel computer with 3D polygon rendering hardware.
It is unknown how many Nürburgring arcade machines still exist. Apparently a "Nürburgring Power-Slide" (N3 P) stands in the Computerspielemuseum Berlin (computer games museum of Berlin, Germany). On flickr a person "videogamescgi1" uploaded detail photos of a "Nürburgring/1" (N1) including screenshot and PCB rack, those look fresh and suggest that it survived in collector's hands. With the early professional simulators it is hard to guess if some still rot in the cellars of some driving school because nobody seems to care. It would be exciting to emulate at least the graphics engine of the early models - and be it only for art purpose because they were way beyond its time.
Video Game Museum in Berlin (photo of Nürburgring 3 Power-Slide)
Interesting stuff, CO, I was watching the moving pictures until I realized that you can auto translate the German captions into English.
Too bad they had that "youth protection law".
==================
Back to the GT10:
I tried to put a POT (potentiometer) into the netlist, where there's supposed to be a pot to adjust the game timer.
There's a bunch of questions about pots that I don't quite understand:
I tried to use POT but that used three terminals, the POT2 only has two terminals.
You can specify a resistance:
POT2(R72, RES_K(10)) // should be a potentiometer 1M // lemans calls it R40 // Less resistance, faster count POT2(R71, RES_K(60)) // lemans calls it R39 // should be 220K , we'll set to less because we want to see it count
and you can specify a dial setting:
PARAM(R72.DIAL,1.0) PARAM(R71.DIAL,1.0)
and in the driver you have to specify an analog input:
So I'm confused as to how the value given in the adjuster affects the POT2. Here I've given the one adjuster a value of 1000, and another a value of 220, how does that change the value, is it a percentage on scale of 0 to 100?
The dial setting in the param, is that an initial value?
==========================
I seem to be able to affect the pot value in-game make it count slower/faster, but the exact semantics are not quite understood.
============================
The UI not working properly with a slow netlist is getting problematic since half the time I can't even bring up the Sliders screen to change the values. Sometimes I can "kick it loose" by hitting ALT+ENTER to swap between full screen and window mode, but it still will hang up. (It will still respond to ALT+F4 and ALT+Enter). It seems like it won't respond to the up/down arrows until after a delay (like it needs the auto-repeat to kick in before it will see it). Does the input buffer get cleared before it can see the events or something?
I'm using Ubuntu, is this just a problem under linux?
one workaround I just figured out, put the netlist game into pause before bringing up the menu, then the menus work.
Interesting stuff, CO, I was watching the moving pictures until I realized that you can auto translate the German captions into English.
Too bad they had that "youth protection law".
The last arcade videogame machine I saw in a German shopping center in Bremen was an Atari "Pole Position" cockpit at Horten that remained in operation likely until about 1992. But I also remember a Sega "Outrun" cockpit at Brinkmann that stood for few weeks in the computer department at the same time when they sold a PC with built-in Megadrive (looking like "Amstrad Mega PC" which was made 1993 - possibly rebranded as Compaq or such). It is unknown if there was a grace period for machines bought earlier or individual special approvals for non-violent games in some states of Germany.
The Nürburgring 3 graphics seems to be mostly described in the German language patent DE3243574A1 while the US patent is more general. The earlier Nürburgring 1 & 2 (which is mostly analogue without CPU) is described in patents US4077138, US4077138A. Unfortunately some of the old Foerst patents seem to be missing in online search. (I don't know if due to invention secrecy act if these included military simulators, but likely they were simply not scanned by Google when not found because these were expired anyway. The German ones tend to be OCR only, thus lacking illustrating drawings like schematics.)
There is a Foerst company history PDF (31MB huge) with plenty of fairly detailed info and many screenshots and newspaper articles of their historical arcade and professional driving simulators.
Particularly the N6 graphics looks gorgeous. Unfortunately the screenshot dates seem partially messed up, and those dates in the youtube video got shuffled even worse and have nothing to do with the spoken text. Here is some additional driving simulator history:
Stunt Cycle is very similar to Gran Trak in many ways, thought I'd fiddle a little.
Attaching a clock to the score up counter and hacking a "force start" button gets you from 8 to 27 buses: (don't know what's happening with the ramps on score = 12)
Okay, got the bike looking normal, still falling...
Don't know if this is right, but JD left a clue, saying that it could be DIRECTION_Q and it didn't look right until I used DIRECTION_Q.
Code
TTL_7402_NOR(D4_3, DIRECTION_Q, H6) // Schematic says DIRECTION, maybe DIRECTION_Q?
TTL_7408_AND(B5_1, DIRECTION_Q, H5_AND_H6) // Schematic says DIRECTION, maybe DIRECTION_Q?
TTL_7402_NOR(D4_4, D4_3.Q, B5_1.Q)
TTL_7400_NAND(H2_1, HSYNC_Q, D4_4.Q)
TTL_7400_NAND(H2_2, HSYNC_Q, H5)
//"TTL_9322","+SELECT,+A1,+B1,+A2,+B2,+A3,+B3,+A4,+B4,+STROBE,@VCC,@GND")
TTL_9322(J1, HSYNC, V4, P, V3, 4V, V2, 2V, V1, 1V, GROUND)
// 1V is from the VERTICAL COUNT registers (screen, used for bus images, read during hsync)
// V1 is from the VERTICAL MOTION registers (relative to the start of the cycle)
// PROM_82S115(name, CE1Q, CE2, A0, A1, A2, A3, A4, A5, A6, A7, A8, STROBE)
PROM_82S115(hf1, GROUND, P, A0, A1, A2, A3, A4, A5, A6, A7, A8, P)
// 9 bits of rom addressing, 512 bytes
ALIAS (A0, J1.Y4) // image row number 0..15
ALIAS (A1, J1.Y3)
ALIAS (A2, J1.Y2)
ALIAS (A3, J1.Y1)
ALIAS (A4, H2_2.Q) // horiz byte number 0..3 (0-2 = bike 3 = bus)
ALIAS (A5, H2_1.Q)
ALIAS (A6, R2) // image number 0..3 (cycle angle)
ALIAS (A7, R3)
ALIAS (A8, R4) // animation frame 0..1
// R2 and R3 are CYCLE ANGLE CONTROL
//
// R2 R3
// 0 0 both wheels on the road
// 1 0 half wheelie (held to a half wheelie while jumping)
// 0 1 full wheelie
// 1 1 crash / flipped position
// depends on the throttle
You have to put a routine into two different locations in the code.
This way I can just copy and paste the same line DEBUGNLAO(cycleresetq,CYCLE_RESET_Q) and it will make the appropriate NETDEV_ANALOG_CALLBACK_MEMBER or NETLIST_ANALOG_OUTPUT.
One thing that's interesting in the debugger, if you "gt 1" you can see the strip of screen that gets painted during the millisecond. It shouldn't be too hard to make it possible to step by less than a millisecond.
Is the chip (AY-3-8760?) inside the "Stunt Cycle" home console based on similar logic gate circuit or is it some kind of microcontroller?
It's at least pretty certain it's not a microcontroller - microcontrollers that could actually do TV-based graphics didn't exist back then. There's no telling if it's based on "similar logic gate circuits" or not. It's an ASIC, all of the one-off TV game systems of that era were. It would require someone decapping such a chip and actually tracing out the silicon to work out an equivalent schematic to know anything more than that.
// TTL_74193(name, A, B, C, D, CLEAR, LOADQ, CU, CD)
// TTL_74193(L8, FIXED_RAMP_ZONE_Q, FIXED_RAMP_ZONE, P, P, JUMP_Q, LOAD_Q, P, GRAVITY) // Schematics say C and D should be to VCC - maybe ground instead?
// changed to GROUND and bike jumps!!!
TTL_74193(L8, FIXED_RAMP_ZONE_Q, FIXED_RAMP_ZONE, GROUND, GROUND, JUMP_Q, LOAD_Q, P, GRAVITY) // Schematics say C and D should be to VCC - maybe ground instead?
I also tried to model a bunch of stuff that leads up to the SPEED_PULSES and up to R38 and R39 but it ran super slow...like over a minute to generate a single frame.
It's interesting to study the different Atari TTL games of this period as they have quite a bit of similarities.
I thought I'd try to figure out how the rom in tank works too:
Code
f = io.open("/mnt/z/mame/roms/tank/90-2006.k10","r") a = f:read("*a")
function prtbyte(adr,a) if adr then io.write(string.format("%x",adr)..":") end for i=7,0,-1 do if (a & (2^i)) ~= 0 then io.write(i) else io.write(" ") end end end
function bit(x,y) return (x >> y) & 1 end
for score=0,63 do for v=0,7 do adr=((score<<3) | v | (1<<10)) io.write(score.." "..v..":") prtbyte(adr, a:byte(adr+1)) print() end end
for dir=0,31 do for v=0,7 do for h = 0,1 do adr=(v | bit(dir,4)<<3 | h<<4 | (dir & 0xf) << 5 | 1 << 10 | 1 << 9) prtbyte(nil,a:byte(adr+1)) end print() end print(string.rep("=",16)) end
for v=0,31 do for h=0,15 do adr=((v<<4) | h | (0<<9)) if h~=7 then prtbyte(nil,a:byte(adr+1)) end end print() end -- playfield 0
for v=0,31 do for h=0,15 do adr=((v<<4) | h | (1<<9)) if h~=7 then prtbyte(nil,a:byte(adr+1)) end end print() end -- playfield 1
So one of the things that had me baffled about tank was figuring out how it handles the mines. Specifically, how does it keep track of whether a mine has been hit or not?
It doesn't have a cpu, and it only has a ROM chip, no ram chips, so how does it know if a mine has been hit?
There's a dual 8 bit shift register, the Signetics N8277, that is used as a 16 bit shift register and that's enough to hold 1 bit of state for each mine.
In the playfield, there's a rectangular region defined for the mines, any bit that's set in this region will be a mine, but we're only allowed 1 mine per row.
When the game starts, the shift register is cleared, a 1 bit means that the mine has been hit.
As the playfield gets scanned vertically, this shift register gets cycled through and updated, collision with either tank1 or tank2 will remove the mine (set the bit to 1 that will get shifted in on the next clock). The D0A and D1A are set so that it inverts the input DSA on pin 4.
I managed to get one of the tanks displaying. It's super hacky but at least it shows something.
There's something very very wrong with one of the 74107's. Hooked up normally, IC_N1.6 GOES TO IC_M1.4 but for some reason, it never gets updated. If I put a debug net list analog output on IC_N1.6, it never gets updated, however if I put a debug net list analog output on IC_N1.5, it magically starts updating??? If it never updates properly THR1Q and THW1Q don't activate and the display window doesn't get activated.
Here you see a tank in the lower right, it jumps all around:
Got both tanks appearing, but jumping all over the place:
So modifying my PCBASIC program you can see the mine image, what's kind of interesting is that it doesn't occupy the entire 16x16 area, just the lower right 8x8.
Code
10 DIM A(5000),B(5000)
50 DEF FN cnot(xarg)=(xarg=0)*-1
60 DEF FN cbool(xarg)=(xarg<>0)*-1
70 DEF FN bit(xarg,yarg)=fn cbool( xarg and (2^yarg))
80 def fn cxor(xarg,yarg)=fn cbool(fn cbool(xarg) xor fn cbool(yarg))
90 DEF FN cnand(xarg,yarg)=fn cbool(fn cnot(fn cbool(xarg) and fn cbool(yarg)))
91 DEF FN cnor(xarg,yarg)=fn cbool(fn cnot(fn cbool(xarg) or fn cbool(yarg)))
100 SCREEN 7 : rem screen 2 too much resolution
101 SIZE=16
105 FOR FLASH = 0 TO 1
110 FOR X = 0 TO SIZE-1
120 FOR Y = 0 TO SIZE-1
130 P = FN cnand ( FN cxor( FN cxor( FN cxor(FN bit(y,2),FN bit(y,3)), FN cxor(FN bit(x,2),FN bit(x,3))), flash), FN cnor( FN cxor( FN bit(y,1),FN bit(x,1)), FN cnor( FN cxor(FN bit(x,2),FN bit(x,3)), FN cxor(FN bit(y,2),FN bit(y,3)) ) ) )
140 PSET(X,Y),FN CNOT(P)*4
150 NEXT : NEXT
155 IF FLASH = 0 THEN GET (0,0)-(SIZE-1,SIZE-1),A ELSE GET(0,0)-(SIZE-1,SIZE-1),B
160 NEXT
200 FOR I = 1 TO 500
210 FLASH = I MOD 2
220 IF FLASH = 1 THEN PUT(160-(size/2),100-(size/2)),A,PSET ELSE PUT (160-(size/2),100-(size/2)),B,PSET
225 FOR DELAY = 1 TO 10000:NEXT
230 NEXT
130 F13.11 = FN CXOR( FN BIT(X,1), FN BIT(X,2) ) : F13.8= FN CXOR( FN BIT(Y,1), FN BIT(Y,2) ) : F13.6 = FN CXOR( F13.8, F13.11) : A10.11= FN CNAND( FN BIT(Y,3), FN BIT(X,3) ) : D7.02 = FN CNOR( A10.11, F13.6) : P = D7.02
140 PSET(X,Y),FN CBOOL(P)*4
If I change the colors a little, you can see it only occupies the lower right quadrant.
I tried everything I could think of to make the tanks stop jumping around, finally I figured that if you hardwire the stop code to the counter you can make it stop:
These values were found by experimentation:
Horizontal load: I think it should be 0x78 but seems to hold steady if I load it with 0x7c. (0x400 - 0x78 = 904) (0x400 - 0x7c = 900)
Took a look at some of the other atari ttl games and it's super cool that a lot of them have detailed manuals. It's useful to read other manuals for the mechanics of each of these games have significant similarities.
I got interested in Atari Anti aircraft and it looks very doable. It seems a bit simpler than other games, so I'll see if I can make it work with netlist.
Engineering started on the concept as well as several other games, including a new military game called Jet Fighter by Lyle Rains. Unfortunately during the process, several of the engineers became disillusioned with Atari and the management and thought they could do a better job. Taking plans and parts from Atari, they started up their own company - Fun Games, Inc.
With the plans and parts stolen, Fun Games released an exact copy of Bristow's Tank by the end of the year, even calling it Tankers. But it was the game announced that year and another released next year that proved their downfall. Released at the same time as Tankers, Take 7 was a compendium of six of Atari's previous PONG efforts and included one extra game called "Bust Out", where a player used a paddle and ball to break through a multi-segmented wall. Bust Out was in fact Breakout. In 1976 they released Race!, a copy of Gran Trak 10, and Biplane, a copy of Lyle's just released Jet Fighter that replaced the jets with biplanes. Atari had enough and sued Fun Games over the Jet Fighter rip off, with Steve Bristow and others testifying. Easily winning the suit, 1976 was the last year a game with the Fun Games banner was released. The damage was done however, and development on Breakout had been stalled during the process.
Got the Antiairc score working and the launchers showing. Don't know why there's 3 vertical copies though.
Finally figured out what "Display Monitor Sliders" does in the Machine Configuration for fixed frequency monitors: It will ignore the sliders when this is set to off.
The score display circuit in stunt cycle and antiairc are very similar, however, stunt cycle uses the YQ output and antiairc uses the Y output. That took me a while to realize what was wrong.
sharkf = io.open("/mnt/z/mame/roms/sharkjaws/004182.da1","r") s = sharkf:read("*a") print(#s)
sharkf2 = io.open("/mnt/z/mame/roms/sharkjaws/004183.db1","r") s2 = sharkf2:read("*a") print(#s2)
function prtbytewide(adr,a,wide) if adr then io.write(string.format("%x",adr)..":") end for i=7,0,-1 do for dup = 1,wide do if (a & (2^i)) ~= 0 then io.write(i) else io.write(" ") end end end end
for image = 0,1 do for line=0,15 do for h = 0,3 do adr = h<<5 | line<<1 | image << 7 prtbytewide(nil, s:byte(adr+1) << 4| s:byte(adr+1+1) << 0, 4) end print() end end -- shark
for image = 0,1 do for line=0,15 do for h = 0,3 do adr = h<<5 | line<<1 | image << 7 prtbytewide(nil, s2:byte(adr+1) << 4 | s2:byte(adr+1+1)<<0,2 ) end print() end end -- diver