I am not sure which problem you are trying to solve. What you propose would relieve you in no way from the obligation to generate sync pulses with cycle-accurate timing, and thus count all cycles consumed by the program. Of course if you would have an independently functioning video interface, you would not have that problem. But what you propose doesn't provide that, and such an interface would approximately have the same complexity as the entire CPU.stegemma wrote:For the screen output, why not use a char map instead of full graphics? It would avoid a lot of problems in the code, I think. maybe a double mode can be implemented, with a little circuit (the single pixel becomes an index in memory+an index for row/col).
The Gigatron project
Moderators: hgm, Rebel, chrisw
-
- Posts: 27817
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: The Gigatron project
-
- Posts: 27817
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: The Gigatron project
I now use the coding strategy to store almost all tables vertically, and index them through the Y register. For 2-dimensional tables, such as leaperAttacks[stm][square] and neighbor[direction][square], each board-sized layer (player or direction) would be stored vertically, and the first index would go in the X register, to identify the layer in an [Y,X] addressing mode. (If the loop over layers is not unrolled, which would turn each layer index into a constant.)
Only tables that are indexed by bit sets will be stored horizontally. (Because they sometimes need more than 128 entries, and because they start at index 0, and I want to leave some completely free pages at the low end of memory.
For local variables I will use the zero page, and copy / restore that to a save place (software stack) when recursing. With the unmodified Gigatron this sseems the fastest way to do it; accessing variables directly on a stack would require loading the frame pointer in Y for every access, and usually there are may accesses for a single recursive call. Y is very heavily used already, and usig it to address a local variable would probably destroy something useful in there that would have to be reloaded afterwards. As long as there is no mode [const,X], (one possible hardware extension), X is useless as frame pointer. The problem would be best solved by a zero-page relocatable through a Z register, but that requires an even more complex hardware extension. And the code using the zero page for local variables would work without modification after such an extesion, after replacing the code to save the zero page to a stack would be replaced by simply loading the Z register with a fresh page address.
This produces code (for the capture-generating C code I posted above) like:
Only tables that are indexed by bit sets will be stored horizontally. (Because they sometimes need more than 128 entries, and because they start at index 0, and I want to leave some completely free pages at the low end of memory.
For local variables I will use the zero page, and copy / restore that to a save place (software stack) when recursing. With the unmodified Gigatron this sseems the fastest way to do it; accessing variables directly on a stack would require loading the frame pointer in Y for every access, and usually there are may accesses for a single recursive call. Y is very heavily used already, and usig it to address a local variable would probably destroy something useful in there that would have to be reloaded afterwards. As long as there is no mode [const,X], (one possible hardware extension), X is useless as frame pointer. The problem would be best solved by a zero-page relocatable through a Z register, but that requires an even more complex hardware extension. And the code using the zero page for local variables would work without modification after such an extesion, after replacing the code to save the zero page to a stack would be replaced by simply loading the Z register with a fresh page address.
This produces code (for the capture-generating C code I posted above) like:
Code: Select all
05A8 /* Capture generation */
05A8 00FC pawnStep = sliderAttacks // uses 4 unused entries in sliderAttacks
05A8 PAGE
0600 moveLoop:
0600 0011 LDA #KING+1 // piece list starts at King
0601 B555 SUB stm,Y
0602 09F2 LDA Y:next // victim = next[KING+1-stm] // but we already checked for King capture, so skip
0603 D55F TAY z_victim // to Y and memory
0604 09EE LDA Y:location // location[victim]
0605
0605 xlp0:
0605 D55A TAY z_to // Y = to = location[victim]
0606 00FE LDA #leaperAttacks
0607 9155 ADD stm,X // X = leapAttacks[stm]
0608 0D00 LDA [Y:X] // leapSet = leapAttacks[stm][to]
0609 F01F BEQ noleaps // while(leapSet) {
060A D100 TAX leapSet
060B
060B xlp1:
060B 1409 LDA set2leaper:X // piece = set2leaper[leapSet]
060C 0D00
060C F413 BGE isPawn // if(piece < 0)
060D 9555 ADD stm,Y // piece += stm
060E
060E isPiece:
060E 207F AND #$7F // piece &= 0x7F // strip piece/pawn flag
060F D55E TAY z_piece // writes to Y and RAM
0610 09EE LDA Y:location // from = location[piece]
0611 FC18 BRA doleap
0612 C259 STA z_from
0613
0613 isPawn: // else
0613 09FC LDA Y:pawnStep // step = pawnStep[piece]
0614 815A ADD z_to
0615 D559 TAY z_from // from = to + step
0616 09FF LDA Y:board
0617 C25E STA z_piece // piece = board[from]
0618
0618 doleap:
0618 FC49 BRA SearchMove // if(SearchMove()) goto cutoff; /* does not return here on cutoff */
0619 011A LDA here+1
061A
061A 0100 LDA leapSet // leapSet = leapSet & leapSet-1
061B A001 SUB #1
061C 2100 AND leapSet
061D EC0B BNE xlp1
061E D100 TAX leapSet // writes to X and RAM
// }
061F
// now do sliders
061F noleaps:
061F 00FC LDA #sliderAttacks
0620 9155 ADD stm,X // X = slideAttacks[stm]
0621 155A LDY z_to
0622 0D00 LDA [Y:X] // slideSet = slideAttacks[stm][to]
0623 F043 BEQ noslides // if(slideSet)
0624 D100 TAX slideSet // to X and memory
0625
0625 0000 LDA #0
0626 C200 STA attackers // attackers = 0 // will collect slider attackers
0627
0627 xlp2: // do {
0627 1408 LDA set2dir:X // views = set2dir[slideSet] // required layer of neighbor[dir][sqr] table
0628 0D00
0628 1200 LDX A
0629 155A LDY z_to
062A 0D00 LDA [Y:X] // from = views[to]
062B 1600 LDY A
062C 09FF LDA Y:board // piece = board[from]
062D 1600 LDY A
062E 09EC LDA Y:attBit // attackers |= attBit[piece]
062F 4100 ORA attackers
0630 C200 STA attackers
0631 0100 LDA slideSet // slideSet = stripBit[slideSet]
0632 A001 SUB #1
0633 2100 AND slideSet
0634 EC27 BNE xlp2 // } while(slideSet)
0635 D100 TAX slideSet
0636
0636 1100 LDX attackers
0637 xlp3: // do {
0637 140A LDA set2slider:X // piece = set2slider[attackers]
0638 0D00
0638 8155 ADD stm
0639 D55E TAY z_piece
063A 09EE LDA Y:location // from = location[piece]
063B C259 STA z_from
063C FC49 BRA SearchMove // if(SearchMove()) goto cutoff
063D 003E LDA #here+1
063E 0100 LDA attackers // attackers = stripBit[attackers]
063F A001 SUB #1
0640 2100 AND attackers
0641 EC37 BNE xlp3 // } while(attackers)
0642 D100 TAX attackers
0643
0643 noslides:
0643 155F LDY z_victim // victim = next[victim]
0644 09F2 LDA Y:next
0645 D55F TAY z_victim // to Y and memory
0646 A100 SUB threshold
0647 E405 BGT xlp0 // while(victim > futilityThreshold)
0648 09EE LDA Y:location
0649 futile:
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
Re: The Gigatron project
With a characters map you don't waste so much memory and you can put code almost everywhere, after the video memory. Maybe it is possible to have a separated video+characters map memory, that can be used for video displaying, leaving full access to program memory. This could avoid interleaving code with video displaying, almost while you're not accessing video memory. Of course I'm not an engeneer and I have no idea if this is possible or not, in this specific project.hgm wrote:I am not sure which problem you are trying to solve. What you propose would relieve you in no way from the obligation to generate sync pulses with cycle-accurate timing, and thus count all cycles consumed by the program. Of course if you would have an independently functioning video interface, you would not have that problem. But what you propose doesn't provide that, and such an interface would approximately have the same complexity as the entire CPU.stegemma wrote:For the screen output, why not use a char map instead of full graphics? It would avoid a lot of problems in the code, I think. maybe a double mode can be implemented, with a little circuit (the single pixel becomes an index in memory+an index for row/col).
Author of Drago, Raffaela, Freccia, Satana, Sabrina.
http://www.linformatica.com
http://www.linformatica.com
-
- Posts: 27817
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: The Gigatron project
The problem is not memory. The chess board I posted only takes 5KB out of 32KB to store it pixel-wise. The problem is that the CPU is fully tied up sending the video signal to the output port, as the video pixel frequency is exactly the same as the instruction execution rate. So every instruction must send a new pixel to the output. This could be reduced, as you suggest, by adding hardware behind the output port to translate an output to multiple pixes, one line from a lookup table of a character generator ROM. A simpler way to do this would be to abandon color, and make the output a shift register, so that every output operation can deliver 6 pixels. (Two output lines need to be reserved for the sync pulses.) Then you would only have to supply an output every 6 instructions.
But 6 instructions is not enough to do a task switch forth and back between video generation and something else. The video output uses both X and Y register, so that you would have to save and reload those if your other task wanted to use them, which already takes more than the 5 instructions you have in between. So you could still only do something else than video generation on a scan line that is entirely dark. There the only task is to generate the sync pulse at the end of the line, and you have about 160 instructions for useful work before you need to generate one. But you will have to generate it exactly in time, meaning yu u must be sure to execute exactly the right number of instructions befor you make it. Which is difficult if your code contains loops.
But 6 instructions is not enough to do a task switch forth and back between video generation and something else. The video output uses both X and Y register, so that you would have to save and reload those if your other task wanted to use them, which already takes more than the 5 instructions you have in between. So you could still only do something else than video generation on a scan line that is entirely dark. There the only task is to generate the sync pulse at the end of the line, and you have about 160 instructions for useful work before you need to generate one. But you will have to generate it exactly in time, meaning yu u must be sure to execute exactly the right number of instructions befor you make it. Which is difficult if your code contains loops.
-
- Posts: 27817
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: The Gigatron project
Some more sample code, this time for the routine that updates the attack map for the moves of the moved or captured piece. Basically it is just a switch on (colored) piece type, which then changes the attack flags at all capture targets.
Code: Select all
045F /* Attack generation: ultimately what can capture what is determined here. */
045F /* The attacks of a piece of a given type from a given square are recorded */
045F /* in the entries of the applicable attack map for the attacked square, */
045F /* as a bit flag. The update routine flips those flags, and can thus be */
045F /* used both to apply and to remove them. This will have to be done */
045F /* to remove the attacks of the moved piece from its old location; add those */
045F /* from its new location, and remove the attacks of the captured piece. */
045F /* That is just for 3 pieces, but it has to be done again for UnMake() */
045F /* This does not sound much more efficient as generating the moves for all */
045F /* pieces from scratch. But the gain comes from the fact that it can be */
045F /* done one ply in advance, when the move was made from the grandparent, */
045F /* as the attacks only become relevant when we can move the piece again. */
045F /* So we will avoid doing this in nodes that have no grand children. */
045F /* Still, it is time-critical code, and for that reason loops are unrolled. */
045F
045F 0001 dbit0 = 1
045F 0002 dbit1 = 2
045F 0004 dbit2 = 4
045F 0008 dbit3 = 8
045F 0010 dbit4 = $10
045F 0020 dbit5 = $20
045F 0040 dbit6 = $40
045F 0080 dbit7 = $80
045F
045F 000A step1 = WIDTH
045F 0001 step3 = 1
045F 0009 step5 = WIDTH - 1
045F 000B step7 = WIDTH + 1
045F FFF6 step2 = -step1
045F FFFF step4 = -step3
045F FFF7 step6 = -step5
045F FFF5 step8 = -step7
045F
045F 000C hip1 = WIDTH + 2
045F 0015 hip2 = 2*WIDTH+1
045F 0013 hip3 = 2*WIDTH-1
045F 0008 hip4 = WIDTH - 2
045F FFF4 hip5 = -hip1
045F FFEB hip6 = -hip2
045F FFED hip7 = -hip3
045F FFF8 hip8 = -hip4
045F
// parameters
045F 0042 g_sqr = tmp
045F 0043 g_bit = tmp+1
045F 0044 g_piece = tmp+2
045F
045F PAGE
0500 FlipAttacks:
0500 C241 STA retad
0501 1544 LDY g_piece
0502 09E7 LDA Y:attgen // get its move-gen routine
0503 FE00 BRA [A] // and execute it
0504
0504 QueenWhite:
0504 FC12 BRA OrthSlide
0505 10FC LDX #sliderAttacks
0506 QueenBlack:
0506 10FD LDX #sliderAttacks+1
0507 QueenSlide:
0507 0141 LDA retad
0508 C242 STA tmp // set up DiagSlide to return here
0509 000C LDA #here+3
050A C241 STA retad
050B FC2D BRA DiagSlide
050C 0142 LDA tmp
050D FC12 BRA OrthSlide // and then continue with OrthSlide
050E C241 STA retad
050F
// Rook
050F OrthWhite:
050F FC12 BRA OrthSlide
0510 10FC LDX #sliderAttacks
0511 OrthBlack:
0511 10FD LDX #sliderAttacks+1
0512 OrthSlide:
0512 1542 LDY g_sqr
0513 0900 LDA Y:views0u
0514 0D00 LDA [Y:X]
0515 6001 XOR #dbit0
0516 CE00 STA [Y:X]
0517 1542 LDY g_sqr
0518 0900 LDA Y:views0d
0519 1600 LDY A
051A 0D00 LDA [Y:X]
051B 6002 XOR #dbit1
051C CE00 STA [Y:X]
051D 1542 LDY g_sqr
051E 0900 LDA Y:views1u
051F 1600 LDY A
0520 0D00 LDA [Y:X]
0521 6004 XOR #dbit2
0522 CE00 STA [Y:X]
0523 1542 LDY g_sqr
0524 0900 LDA Y:views1d
0525 1600 LDY A
0526 0D00 LDA [Y:X]
0527 6008 XOR #dbit3
0528 FD41 BRA [retad]
0529 CE00 STA [Y:X]
052A
// Bishops
052A DiagWhite:
052A FC12 BRA OrthSlide
052B 10FC LDX #sliderAttacks
052C DiagBlack:
052C 10FD LDX #sliderAttacks+1
052D DiagSlide:
052D 1542 LDY g_sqr
052E 0900 LDA Y:views2u
052F 1600 LDY A
0530 0D00 LDA [Y:X]
0531 6010 XOR #dbit4
0532 CE00 STA [Y:X]
0533 1542 LDY g_sqr
0534 0900 LDA Y:views2d
0535 1600 LDY A
0536 0D00 LDA [Y:X]
0537 6020 XOR #dbit5
0538 CE00 STA [Y:X]
0539 1542 LDY g_sqr
053A 0900 LDA Y:views3u
053B 1600 LDY A
053C 0D00 LDA [Y:X]
053D 6040 XOR #dbit6
053E CE00 STA [Y:X]
053F 1542 LDY g_sqr
0540 0900 LDA Y:views3d
0541 1600 LDY A
0542 0D00 LDA [Y:X]
0543 6080 XOR #dbit7
0544 FD41 BRA [retad]
0545 CE00 STA [Y:X]
0546
0546
0546 /* Leaper attacks: King and each Knight has its own bit in the leaper attack map. */
0546 /* Pawn attacks are just marked by whether they come from the left or right. */
0546 /* The attacks are always recorded, even for empty squares. So we don't have to */
0546 /* worry when a square gets occupied. Even off-board attacks are recorded */
0546 /* (the map includes a 2-wide guard band), so we don't have to test whether */
0546 /* the leaper targets are off board. */
0546 KnightWhite:
0546 FC12 BRA OrthSlide
0547 10FE LDX #leaperAttacks
0548 KnightBlack:
0548 10FF LDX #leaperAttacks+1
0549 KnightAttacks:
0549 1544 LDY g_piece
054A 09EC LDA Y:attBit
054B C243 STA g_bit
054C 0142 LDA g_sqr
054D 940C ADD #hip1,Y
054E 0D00 LDA [Y:X]
054F 6143 XOR g_bit
0550 CE00 STA [Y:X]
0551 0142 LDA g_sqr
0552 9415 ADD #hip2,Y
0553 0D00 LDA [Y:X]
0554 6143 XOR g_bit
0555 CE00 STA [Y:X]
0556 0142 LDA g_sqr
0557 9413 ADD #hip3,Y
0558 0D00 LDA [Y:X]
0559 6143 XOR g_bit
055A CE00 STA [Y:X]
055B 0142 LDA g_sqr
055C 9408 ADD #hip4,Y
055D 0D00 LDA [Y:X]
055E 6143 XOR g_bit
055F CE00 STA [Y:X]
0560 0142 LDA g_sqr
0561 94F4 ADD #hip5,Y
0562 0D00 LDA [Y:X]
0563 6143 XOR g_bit
0564 CE00 STA [Y:X]
0565 0142 LDA g_sqr
0566 94EB ADD #hip6,Y
0567 0D00 LDA [Y:X]
0568 6143 XOR g_bit
0569 CE00 STA [Y:X]
056A 0142 LDA g_sqr
056B 94ED ADD #hip7,Y
056C 0D00 LDA [Y:X]
056D 6143 XOR g_bit
056E CE00 STA [Y:X]
056F 0142 LDA g_sqr
0570 94F8 ADD #hip8,Y
0571 0D00 LDA [Y:X]
0572 6143 XOR g_bit
0573 FD41 BRA [retad]
0574 CE00 STA [Y:X]
0575
0575 KingWhite:
0575 FC12 BRA OrthSlide
0576 10FE LDX #leaperAttacks
0577 KingBlack:
0577 10FF LDX #leaperAttacks+1
0578 KingAttacks:
0578 0142 LDA g_sqr
0579 940A ADD #step1,Y
057A 0D00 LDA [Y:X]
057B 6040 XOR #$40
057C CE00 STA [Y:X]
057D 0142 LDA g_sqr
057E 94F6 ADD #step2,Y
057F 0D00 LDA [Y:X]
0580 6040 XOR #$40
0581 CE00 STA [Y:X]
0582 0142 LDA g_sqr
0583 9401 ADD #step3,Y
0584 0D00 LDA [Y:X]
0585 6040 XOR #$40
0586 CE00 STA [Y:X]
0587 0142 LDA g_sqr
0588 94FF ADD #step4,Y
0589 0D00 LDA [Y:X]
058A 6040 XOR #$40
058B CE00 STA [Y:X]
058C 0142 LDA g_sqr
058D 9409 ADD #step5,Y
058E 0D00 LDA [Y:X]
058F 6040 XOR #$40
0590 CE00 STA [Y:X]
0591 0142 LDA g_sqr
0592 94F7 ADD #step6,Y
0593 0D00 LDA [Y:X]
0594 6040 XOR #$40
0595 CE00 STA [Y:X]
0596 0142 LDA g_sqr
0597 940B ADD #step7,Y
0598 0D00 LDA [Y:X]
0599 6040 XOR #$40
059A CE00 STA [Y:X]
059B 0142 LDA g_sqr
059C 94F5 ADD #step8,Y
059D 0D00 LDA [Y:X]
059E 6040 XOR #$40
059F FD41 BRA [retad]
05A0 CE00 STA [Y:X]
05A1
05A1 WhitePawn:
05A1 10FE LDX #leaperAttacks
05A2 0142 LDA g_sqr
05A3 9409 ADD #step5,Y
05A4 0D00 LDA [Y:X]
05A5 6001 XOR #1
05A6 CE00 STA [Y:X]
05A7 0142 LDA g_sqr
05A8 940B ADD #step7,Y
05A9 0D00 LDA [Y:X]
05AA 6002 XOR #2
05AB FD41 BRA [retad]
05AC CE00 STA [Y:X]
05AD
05AD BlackPawn:
05AD 10FF LDX #leaperAttacks+1
05AE 0142 LDA g_sqr
05AF 94F7 ADD #step6,Y
05B0 0D00 LDA [Y:X]
05B1 6001 XOR #1
05B2 CE00 STA [Y:X]
05B3 0142 LDA g_sqr
05B4 94F5 ADD #step8,Y
05B5 0D00 LDA [Y:X]
05B6 6002 XOR #2
05B7 FD41 BRA [retad]
05B8 CE00 STA [Y:X]
05B9
-
- Posts: 2488
- Joined: Tue Aug 30, 2016 8:19 pm
- Full name: Rasmus Althoff
Re: The Gigatron project
In regard to the final playing level, what do you estimate where the device will land relative to the AVR-Max?
-
- Posts: 27817
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: The Gigatron project
I truly have no idea.
-
- Posts: 859
- Joined: Mon Aug 10, 2009 10:05 pm
- Location: Italy
- Full name: Stefano Gemma
Re: The Gigatron project
What do you use to assemble this code?hgm wrote:Some more sample code, this time for the routine that updates the attack map for the moves of the moved or captured piece. Basically it is just a switch on (colored) piece type, which then changes the attack flags at all capture targets.
Code: Select all
045F /* Attack generation: ultimately what can capture what is determined here. */ 045F /* The attacks of a piece of a given type from a given square are recorded */ 045F /* in the entries of the applicable attack map for the attacked square, */ 045F /* as a bit flag. The update routine flips those flags, and can thus be */ 045F /* used both to apply and to remove them. This will have to be done */ 045F /* to remove the attacks of the moved piece from its old location; add those */ 045F /* from its new location, and remove the attacks of the captured piece. */ 045F /* That is just for 3 pieces, but it has to be done again for UnMake() */ 045F /* This does not sound much more efficient as generating the moves for all */ 045F /* pieces from scratch. But the gain comes from the fact that it can be */ 045F /* done one ply in advance, when the move was made from the grandparent, */ 045F /* as the attacks only become relevant when we can move the piece again. */ 045F /* So we will avoid doing this in nodes that have no grand children. */ 045F /* Still, it is time-critical code, and for that reason loops are unrolled. */ 045F 045F 0001 dbit0 = 1 045F 0002 dbit1 = 2 045F 0004 dbit2 = 4 045F 0008 dbit3 = 8 045F 0010 dbit4 = $10 045F 0020 dbit5 = $20 045F 0040 dbit6 = $40 045F 0080 dbit7 = $80 045F 045F 000A step1 = WIDTH 045F 0001 step3 = 1 045F 0009 step5 = WIDTH - 1 045F 000B step7 = WIDTH + 1 045F FFF6 step2 = -step1 045F FFFF step4 = -step3 045F FFF7 step6 = -step5 045F FFF5 step8 = -step7 045F 045F 000C hip1 = WIDTH + 2 045F 0015 hip2 = 2*WIDTH+1 045F 0013 hip3 = 2*WIDTH-1 045F 0008 hip4 = WIDTH - 2 045F FFF4 hip5 = -hip1 045F FFEB hip6 = -hip2 045F FFED hip7 = -hip3 045F FFF8 hip8 = -hip4 045F // parameters 045F 0042 g_sqr = tmp 045F 0043 g_bit = tmp+1 045F 0044 g_piece = tmp+2 045F 045F PAGE 0500 FlipAttacks: 0500 C241 STA retad 0501 1544 LDY g_piece 0502 09E7 LDA Y:attgen // get its move-gen routine 0503 FE00 BRA [A] // and execute it 0504 0504 QueenWhite: 0504 FC12 BRA OrthSlide 0505 10FC LDX #sliderAttacks 0506 QueenBlack: 0506 10FD LDX #sliderAttacks+1 0507 QueenSlide: 0507 0141 LDA retad 0508 C242 STA tmp // set up DiagSlide to return here 0509 000C LDA #here+3 050A C241 STA retad 050B FC2D BRA DiagSlide 050C 0142 LDA tmp 050D FC12 BRA OrthSlide // and then continue with OrthSlide 050E C241 STA retad 050F // Rook 050F OrthWhite: 050F FC12 BRA OrthSlide 0510 10FC LDX #sliderAttacks 0511 OrthBlack: 0511 10FD LDX #sliderAttacks+1 0512 OrthSlide: 0512 1542 LDY g_sqr 0513 0900 LDA Y:views0u 0514 0D00 LDA [Y:X] 0515 6001 XOR #dbit0 0516 CE00 STA [Y:X] 0517 1542 LDY g_sqr 0518 0900 LDA Y:views0d 0519 1600 LDY A 051A 0D00 LDA [Y:X] 051B 6002 XOR #dbit1 051C CE00 STA [Y:X] 051D 1542 LDY g_sqr 051E 0900 LDA Y:views1u 051F 1600 LDY A 0520 0D00 LDA [Y:X] 0521 6004 XOR #dbit2 0522 CE00 STA [Y:X] 0523 1542 LDY g_sqr 0524 0900 LDA Y:views1d 0525 1600 LDY A 0526 0D00 LDA [Y:X] 0527 6008 XOR #dbit3 0528 FD41 BRA [retad] 0529 CE00 STA [Y:X] 052A // Bishops 052A DiagWhite: 052A FC12 BRA OrthSlide 052B 10FC LDX #sliderAttacks 052C DiagBlack: 052C 10FD LDX #sliderAttacks+1 052D DiagSlide: 052D 1542 LDY g_sqr 052E 0900 LDA Y:views2u 052F 1600 LDY A 0530 0D00 LDA [Y:X] 0531 6010 XOR #dbit4 0532 CE00 STA [Y:X] 0533 1542 LDY g_sqr 0534 0900 LDA Y:views2d 0535 1600 LDY A 0536 0D00 LDA [Y:X] 0537 6020 XOR #dbit5 0538 CE00 STA [Y:X] 0539 1542 LDY g_sqr 053A 0900 LDA Y:views3u 053B 1600 LDY A 053C 0D00 LDA [Y:X] 053D 6040 XOR #dbit6 053E CE00 STA [Y:X] 053F 1542 LDY g_sqr 0540 0900 LDA Y:views3d 0541 1600 LDY A 0542 0D00 LDA [Y:X] 0543 6080 XOR #dbit7 0544 FD41 BRA [retad] 0545 CE00 STA [Y:X] 0546 0546 0546 /* Leaper attacks: King and each Knight has its own bit in the leaper attack map. */ 0546 /* Pawn attacks are just marked by whether they come from the left or right. */ 0546 /* The attacks are always recorded, even for empty squares. So we don't have to */ 0546 /* worry when a square gets occupied. Even off-board attacks are recorded */ 0546 /* (the map includes a 2-wide guard band), so we don't have to test whether */ 0546 /* the leaper targets are off board. */ 0546 KnightWhite: 0546 FC12 BRA OrthSlide 0547 10FE LDX #leaperAttacks 0548 KnightBlack: 0548 10FF LDX #leaperAttacks+1 0549 KnightAttacks: 0549 1544 LDY g_piece 054A 09EC LDA Y:attBit 054B C243 STA g_bit 054C 0142 LDA g_sqr 054D 940C ADD #hip1,Y 054E 0D00 LDA [Y:X] 054F 6143 XOR g_bit 0550 CE00 STA [Y:X] 0551 0142 LDA g_sqr 0552 9415 ADD #hip2,Y 0553 0D00 LDA [Y:X] 0554 6143 XOR g_bit 0555 CE00 STA [Y:X] 0556 0142 LDA g_sqr 0557 9413 ADD #hip3,Y 0558 0D00 LDA [Y:X] 0559 6143 XOR g_bit 055A CE00 STA [Y:X] 055B 0142 LDA g_sqr 055C 9408 ADD #hip4,Y 055D 0D00 LDA [Y:X] 055E 6143 XOR g_bit 055F CE00 STA [Y:X] 0560 0142 LDA g_sqr 0561 94F4 ADD #hip5,Y 0562 0D00 LDA [Y:X] 0563 6143 XOR g_bit 0564 CE00 STA [Y:X] 0565 0142 LDA g_sqr 0566 94EB ADD #hip6,Y 0567 0D00 LDA [Y:X] 0568 6143 XOR g_bit 0569 CE00 STA [Y:X] 056A 0142 LDA g_sqr 056B 94ED ADD #hip7,Y 056C 0D00 LDA [Y:X] 056D 6143 XOR g_bit 056E CE00 STA [Y:X] 056F 0142 LDA g_sqr 0570 94F8 ADD #hip8,Y 0571 0D00 LDA [Y:X] 0572 6143 XOR g_bit 0573 FD41 BRA [retad] 0574 CE00 STA [Y:X] 0575 0575 KingWhite: 0575 FC12 BRA OrthSlide 0576 10FE LDX #leaperAttacks 0577 KingBlack: 0577 10FF LDX #leaperAttacks+1 0578 KingAttacks: 0578 0142 LDA g_sqr 0579 940A ADD #step1,Y 057A 0D00 LDA [Y:X] 057B 6040 XOR #$40 057C CE00 STA [Y:X] 057D 0142 LDA g_sqr 057E 94F6 ADD #step2,Y 057F 0D00 LDA [Y:X] 0580 6040 XOR #$40 0581 CE00 STA [Y:X] 0582 0142 LDA g_sqr 0583 9401 ADD #step3,Y 0584 0D00 LDA [Y:X] 0585 6040 XOR #$40 0586 CE00 STA [Y:X] 0587 0142 LDA g_sqr 0588 94FF ADD #step4,Y 0589 0D00 LDA [Y:X] 058A 6040 XOR #$40 058B CE00 STA [Y:X] 058C 0142 LDA g_sqr 058D 9409 ADD #step5,Y 058E 0D00 LDA [Y:X] 058F 6040 XOR #$40 0590 CE00 STA [Y:X] 0591 0142 LDA g_sqr 0592 94F7 ADD #step6,Y 0593 0D00 LDA [Y:X] 0594 6040 XOR #$40 0595 CE00 STA [Y:X] 0596 0142 LDA g_sqr 0597 940B ADD #step7,Y 0598 0D00 LDA [Y:X] 0599 6040 XOR #$40 059A CE00 STA [Y:X] 059B 0142 LDA g_sqr 059C 94F5 ADD #step8,Y 059D 0D00 LDA [Y:X] 059E 6040 XOR #$40 059F FD41 BRA [retad] 05A0 CE00 STA [Y:X] 05A1 05A1 WhitePawn: 05A1 10FE LDX #leaperAttacks 05A2 0142 LDA g_sqr 05A3 9409 ADD #step5,Y 05A4 0D00 LDA [Y:X] 05A5 6001 XOR #1 05A6 CE00 STA [Y:X] 05A7 0142 LDA g_sqr 05A8 940B ADD #step7,Y 05A9 0D00 LDA [Y:X] 05AA 6002 XOR #2 05AB FD41 BRA [retad] 05AC CE00 STA [Y:X] 05AD 05AD BlackPawn: 05AD 10FF LDX #leaperAttacks+1 05AE 0142 LDA g_sqr 05AF 94F7 ADD #step6,Y 05B0 0D00 LDA [Y:X] 05B1 6001 XOR #1 05B2 CE00 STA [Y:X] 05B3 0142 LDA g_sqr 05B4 94F5 ADD #step8,Y 05B5 0D00 LDA [Y:X] 05B6 6002 XOR #2 05B7 FD41 BRA [retad] 05B8 CE00 STA [Y:X] 05B9
Author of Drago, Raffaela, Freccia, Satana, Sabrina.
http://www.linformatica.com
http://www.linformatica.com
-
- Posts: 27817
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: The Gigatron project
I wrote an assembler.
I have not thouroughly tested it, however. I used it to assemble the program to display the Chess board in the emulator, and this seemed to work. But it was not nearly using all existing instructions. Just a fairly small subset.
This assembler has a few nice features that help me preparing for some hardware modifications I plan to make to the version of the Gigatron I will build, once Marcel is ready to deliver the kits. E.g. it understands the addressing mode [const,X], emitting it as [X] when you run with the option -mDX, but replaces it with the pair of instructions "LDY #const; ... [Y,X]" when this option is disabled. And it then keeps track of what is in the Y register during linear code sequences, for supporting an optional reload of Y with the value that had to be destroyed for emulating the [const,X] mode. So it is possible to write code that would work both with and without the -mDX option (but much more efficiently with the latter, if you have that hardware modification).
I also added a pseudo-instruction "SYNC label", which assembles to the sequence of instructions that is needed to test if there still is enough time to execute the code block upto the given label before you must output a video sync pulse. It would then deduct the required execution time from the cycle counter if there is, but would jump to the video handler (posting a return address in the accumulator) if there isn't. The video handler would then delay the remaining time before generating a sync pulse, reset the cycle budget to a full scan line, and return to execute the code block. Something like
I have not thouroughly tested it, however. I used it to assemble the program to display the Chess board in the emulator, and this seemed to work. But it was not nearly using all existing instructions. Just a fairly small subset.
This assembler has a few nice features that help me preparing for some hardware modifications I plan to make to the version of the Gigatron I will build, once Marcel is ready to deliver the kits. E.g. it understands the addressing mode [const,X], emitting it as [X] when you run with the option -mDX, but replaces it with the pair of instructions "LDY #const; ... [Y,X]" when this option is disabled. And it then keeps track of what is in the Y register during linear code sequences, for supporting an optional reload of Y with the value that had to be destroyed for emulating the [const,X] mode. So it is possible to write code that would work both with and without the -mDX option (but much more efficiently with the latter, if you have that hardware modification).
I also added a pseudo-instruction "SYNC label", which assembles to the sequence of instructions that is needed to test if there still is enough time to execute the code block upto the given label before you must output a video sync pulse. It would then deduct the required execution time from the cycle counter if there is, but would jump to the video handler (posting a return address in the accumulator) if there isn't. The video handler would then delay the remaining time before generating a sync pulse, reset the cycle budget to a full scan line, and return to execute the code block. Something like
Code: Select all
LDA #-timeRequired
ADD cycleBudget
STA cycleBudget
BLT VideoHandler
LDA l1 // return address
l1:
// code block