Jump to content

About This Club

For anyone wanting to code on the Atari Lynx. Join, ask questions, show off your work, have fun!
  1. What's new in this club
  2. Every evaluated move has a ranking, I will not over rank a bad move, but randomly under rank the best move so that a less good move (if exists) could be selected. Thanks for your opinion, tonight I'll try to implement it.
  3. I am not really aware of AI algorithm, but I like the idea, it is simple and elegant to give some weakness to the AI. But when it doesn't play the best move it should do, the AI should still make a "good" move, just like someone who did not see the trap and play his strategy. And not a random useless move or it would break the feeling of playing against a good opponent. Maybe you could put this as an option (strong AI vs emotionnal AI :D)
  4. In 4ttude there is a strong AI algorithm, there are 15 rules, every rule has a difficulty level (from 0 to 10) and is applied only if the game level is the same or higher At level 0 the cpu does only random moves, at level 1 blocks a row if three opponent marks, or complete a row of 3 own marks if available. You can beat it setting a simple Atari (two rows with 3 marks). From level 2 the cpu starts stopping two opponent marks in a row and tries to complete a row with 2 of it's marks. At level 2 the game starts to be very difficult because, despite the simple cpu strategy, an humann player can miss some almost winning rows, while the cup never misses one. The good thing of this situation is that the player has to learn to check all the board and this makes him stronger, on the other hand the casual player can get bored by the starting difficulty. To compensate this I'm considering to randomly introduce a 20% of mistakes in evaluating the rules of the same level of the game. This meas that at level 1 the game can miss to block 3 marks in a row one time out of 5. At level 2 it can miss to block 2 marks in a row 1 time of 5, but never misses to block a 3 in row because it is a level 1 rule. What about it. It's better to leave the cpu as strong as possible, or is it a good idea to introduce random mistakes, Do you know if such an idea is used in other games?
  5. Maybe a bit late, but I just came back from hollidays. If I don't sprint like a stupid rabbit to the right, I go through the level pretty each time, with lot of remaining time. My 2 cents : - the sprite legs animation is probably too fast while running - I have the impression to go faster up and down than right and left For these 2 points, it is better when you are a bit injured, feels more natural (but maybe the horizontal scrolling is a bit boring) - if you go down, you can easily go through the level (just be careful with some grounds, but once you get some injuries, it is easy to avoid them because the game is slower), may be put some more obstacles like the trees at the end of level. The collpsing ground sprite is not clear, especially for existing ground the first time. But the overall feeling (sprites, ambiance, gameplay) is good, and this is a really nice level, can't wait to see the full game in action.
  6. grep did not find it?!! Anyway, from what I see, there is no problem to use my routine and skip callirq.s completely. Of course, one has to pay attention that currently only A is saved. If cycle count matters
  7. Besides STP and WAI it seems to be a plain vanilla 65C02, so no need for a special CPU. The multi-byte NOPs are in 65C02.
  8. I had no idea that there is a bbs3 instruction available. Oliver's cc65 has already changed a lot by removing IRQ's from all drivers and changing the joypad syntax. So speeding up the IRQ's a little would not hurt further cc65 use for me... It would be nice to change the CPU directive to use all the special op-codes available for Lynx. /* CPUs */ typedef enum { CPU_UNKNOWN = -1, /* Not specified or invalid target */ CPU_NONE, /* No CPU - for assembler */ CPU_6502, CPU_6502X, /* "Extended", that is: with illegal opcodes */ CPU_65SC02, CPU_65C02, CPU_65816, CPU_SUNPLUS, /* Not in the freeware version - sorry */ CPU_SWEET16, CPU_HUC6280, /* Used in PC engine */ CPU_M740, /* Mitsubishi 740 series MCUs */ CPU_COUNT /* Number of different CPUs */ } cpu_t; Perhaps creating a new CPU type 65SC02X /* All Lynx op-codes plus usable illegal op-codes */ Then we could create cute mnemonics for SKIP1 opcode and who knows what else. The da65 disassembler seems to give mnemonics to 256 different opcodes when you choose 6502X as the CPU type.
  9. Ok, w/o changing too much: irq.s: pha phx phy lda INTSET sta INTSET_ZP jsr callirq ply plx pla rti ... HandyMusic_PCMMain: bbs3 INTSET_ZP, sample_irq clc rts sample_irq: lda #$8 sta INTCLR jsr PCMSample_IRQ sec rts
  10. At cc65/libsrc/runtime/callirq.s They had some great idea to hide the interrupts from confusing programmers. So now we need to use .interruptor everywhere. Fortunately there is a possibility to add higher levels to interrupts. Example: .interruptor _HandyMusic_Main .interruptor _HandyMusic_PCMMain,15 Normal HandyMusic just ticks on while PSC samples are served at highest priority. This was a must to get the correct pitch for the samples. HandyMusic_PCMMain: lda INTSET and #TIMER3_INTERRUPT beq @L0 jsr PCMSample_IRQ sec rts @L0: clc rts As you can see setting CARRY bit tells the IRQ handler that there are no more IRQ handlers to be called for this interrupt. The CLC is the normal flag that lets you hook more handlers to this IRQ. A typical example would be to use VBL for the screen and system time. .interruptor update_clock update_clock: lda INTSET and #%00000100 beq @NotVBlank ; Not vertical-blank interrupt inc clock_count bne @L1 inc clock_count+1 bne @L1 inc clock_count+2 @L1: ;clc ; General interrupt was not reset @NotVBlank: rts
  11. I thought the cc65 irq code was derived from the original BLL code. But yes, this code is ugly. Where is "callirq"?
  12. The BLL IRQ handling is more efficient than cc65. I would like to use this technique instead of having the comparison in every IRQ handler.
  13. Did some measurements (after all these year ... ? ) ; 75Hz | 60Hz | 50Hz ; iter opcode count us cycles | count us cycles | count us cycles | ; of 64us per opcode | of 64us per opcode | of 64us per opcode | ; --------------------------------------------------------------------------------- ; 32K xb,x3 152 0.297 1 | 148 0.289 1 | 145 0.283 1 ; 16K NOP 152 0.594 2 | 147 0.578 2 | 144 0.563 2 ; 16K x2 152 0.594 2 | | ; 16K adc imm 152 0.594 2 | | ; 8K adc zp 130 1.02 3.4 | | ; 8K adc abs 169 1.32 4.4 | 163 1.27 4.4 | ; 8K jmp 122 0.953 3.2 | | ; 8K bra 122 0.953 3.2 | | ; 8K bCC n/t 76 0.598 2.1 | | ; 8K bCC /t 122 0.953 3.2 | | ; 8K $dc,$fc 169 1.32 4.4 | | ; 4K $5c 177 2.77 2.6 | | ; 4K inc abs 130 2.03 6.8 | | ; 4K inc zp 111 1.73 5.8 | | ; n/t not taken ; /t taken One can see the influence of the screen refresh on the length of a cycle and also that the clock of the CPU is ~3.5MHz (not 4MHz). Code: https://github.com/42Bastian/lynx_hacking/tree/master/cycle_check
  14. Hi, diving into the 65C02 docs, I found out "CLD" is not needed in the interrupt handler. After all these year, I did also re-work the interrupt code to save some cycles. ; Timer 7 => 3+2+4+3+2+12 = 26 ; Timer 6 => 3+2+4+2+3+12 = 28 ; UART => 3+2+4+3+6 = 18 ; pre = 19 ; Timer 5 => 19+4*(2+2)+12 = 47 ; Timer 3 => 19+3*(2+2)+(2+3)+12 = 48 ; Timer 2 => 19+2*(2+2)+(2+3)+12 = 44 ; Timer 1 => 19+1*(2+2)+(2+3)+12 = 40 ; Timer 0 => 19+(2+3)+12 = 36 ; Timer 0 => HBL ; Timer 2 => VBL irq:: pha ; 3 lda #$10 ; 2 bit $fd81 ; 4 bne _4 ; 2,3 bmi _7 ; 2,3 bvs _6 ; 2,3 lda $fd81 ; 4 IFD BRKuser beq dummy_irq ENDIF lsr ; 2 bcs _0 ; 2,3 lsr ; 2 bcs _1 ; 2,3 lsr ; 2 bcs _2 ; 2,3 lsr ; 2 bcs _3 ; 2,3 _5: lda #$20 ; 2 sta $fd80 ; 4 jmp (irq_vecs+5*2) ;6 _4: jmp (irq_vecs+4*2) ; 6 _3: lda #$08 sta $fd80 jmp (irq_vecs+3*2) _2: lda #$04 sta $fd80 jmp (irq_vecs+2*2) _1: lda #$2 sta $fd80 jmp (irq_vecs+2*2) _0: lda #$1 sta $fd80 jmp (irq_vecs+0*2) _6: lda #$40 sta $fd80 jmp (irq_vecs+6*2) _7: lda #$80 sta $fd80 jmp (irq_vecs+7*2) dummy_irq END_IRQ Compared to the old one, the worst case cycles are heavily reduced: ;--------------- ;- Interrupt-Handler ;--------------- ; pre = 17 , post = 14 ; Timer 0 => 17+4+2+2+14 = 39 ; Timer 1 => 17+4+2+3+2+2+14 = 44 ; Timer 2 => 17+4+2+2*(3+2)+2+14 = 49 ; Timer 3 => 17+4+2+3*(3+2)+2+14 = 54 ; UART => 22 ; Timer 5 => 17+4+2+5*(3+2)+2+14 = 64 ; Timer 6 => 17+4+2+6*(3+2)+2+14 = 69 ; Timer 7 => 17+4+2+7*(3+2)+2+14 = 74
  15. Your game remember me a little bit of chaos engine (you might remember that one too :)). I think if you could get the same feeling with controls and movements it would greatly help to adjust the difficulty
  16. It helps. Reaching the chopper on 1st try is not good. Hmm... I could create the scenery without spear traps close to the center of damage. Then you could run a bit at full speed to get some distance before the damage starts catching up. The pro's is that I could then speed up the damage wave. It could also be a good thing to add logical obstacles like buildings, trees instead of traps. So many choices. Thanks for trying this out.
  17. It felt much better now, maybe a bit too easy actually (got the copter on the first run this time). The speed could probably be a bit faster, but as long as there is no acceleration when walking. That's the slightly odd part imo. Hope that helps
  18. Thanks. The problem is that as a programmer I get blind to my own games. The max speed is now reduced to half and obviously the breaking ground speed also. Otherwise the task would be impossible. Please use this version for your polls instead of the original one. onduty.lnx
  19. I tried and tried and died and died My 2 cents: Set a constant running speed. At the moment running is quite disturbing. The fact that the player starts fairly slow and then accelerate to an insane speed make the game rly difficult imo. (I played on handy).
  20. In the upcoming Lynx 30 years competition I would be a little interested in the difficulty level. Just so I get the game speed right. Here is a small snippet where you have to run right to get to a chopper. The area is becoming full of pits that cause damage. Can any of you guys make it? Or is the ground collapsing too fast. In order to have a walk-through you can press Opt1 to become invulnerable (007, 077 or 777 - different developer modes) - allows you to walk all the way to the chopper. Just so you see what to area looks like. The chopper will wait at the letter H most right. onduty.lnx
  21. Hard to say. I have not worked with the CL65. But Uz usually writes readable code so it may be worth to have a look. The bad thing is that compatibility with Olivers version would break. Perhaps this is something we should do for mainstream cc65? Edit: from what I have heard the compatibility may already be broken...
  22. The important thing is to not use sei/cli in the interrupt handler. You should be able to extract the relevant part from HandyMusic driver. Just comment out sei/cli commands. With sei/cli masking enabled I was not able to keep up the speed on the real hardware. .interruptor _HandyMusic_PCMMain,15 _PlayPCMSample: PHA ;sei ; Kill IRQs just for a bit to... LDA HandyMusic_Disable_Samples beq permissionGranted ; Make sure we're allowed to play PLA ;cli rts permissionGranted: LDA#$FF STA HandyMusic_Channel_NoWriteBack ; Capture Channel 0 STZ $FD25 STZ $fD0D ; Disable IRQ3 in case sample is already playing ;cli LDA $FD40 STA Sample_PanBackup LDA #$FF STA $FD40 STA _Sample_Playing ; Sample is playing PLA clc ;********************** ; Load File Directory * ;********************** ;ADC#<_SAMPLES_FILENR ; Sample File Offsets ;ldx #0 ;jsr _openn ; Load Directory ;*********************** ; Parse File Directory * ;*********************** ParseDir00: ; LDA _FileExecFlag ; Check to see if exec flag is set ; bne ParseDir01 ; in order to toggle AUDIN ; LDA __iodat ; (For 1MB Card control) ; AND#%11101111 ; STA __iodat ; bra ParseDir02 ;ParseDir01: ; LDA __iodat ; ORA#%00010000 ; STA __iodat ;ParseDir02: lda _FileStartBlock sta _FileCurrBlock ; startblock jsr lynxblock lda _FileBlockOffset eor #$FF tax lda _FileBlockOffset+1 eor #$FF tay jsr lynxskip0 ;******************** ; Setup Timer 3 IRQ * ;******************** LDA#125 STA $fd0c; T3 Backup (125) STA $fd0e; T3 Current LDA#$D8 STA $fd0d; T3 Mode (~8000Hz Reload) rts ;**************************************************************** ; PCMSample_IRQ: * ; Streams a single sample from the cartridge, playing * ; it through the direct volume register of channel 0. * ; Automatically disabled when playback is finished. * ;**************************************************************** PCMSample_IRQ: ;echo "HandyMusic PCM IRQ Address: %HPCMSample_IRQ" ;*************************** ;* Read one byte from Cart * ;*************************** ;PHP ;PHA DEC _FileFileLen bne KeepReading DEC _FileFileLen+1 beq PlayBackDone KeepReading: jsr ReadByte ; Read byte sta $fd22 ; Store to Direct Volume reg bra ExitIRQ PlayBackDone: STZ $fd0d ; Kill IRQ, Sample is finished. STZ _Sample_Playing ; Sample is not playing LDA Sample_PanBackup STA $FD40 STZ HandyMusic_Channel_NoWriteBack ExitIRQ: ;PLA ;PLP rts ;********************************************************** ;* Fetch one byte from cart, reselecting block if needed. * ;********************************************************** ReadByte: ; LDA _FileExecFlag ; AND#2 ; beq LoROMRead ; lda $fcb3 ; bra IncCartByte ;LoROMRead: lda $fcb2 IncCartByte: inc _FileBlockByte bne BailOut inc _FileBlockByte+1 bne BailOut jmp lynxblock BailOut: rts I did not get the directory stuff to work so in the game I set the seek to the right spot before the call. extern void __fastcall__ openn(int filenr); openn((int)&SAMPLES_FILENR); PlayPCMSample(0);
  23. I need double buffering, but if the screen is not changing I can use the inactive. About streaming from cart, I'm going to implement it because I need some sampld sfx without freezing the game.
  24. You could always just set the viewpage and drawpage to the same index 0. Then the lower page (page 1) is free. The new forum cut the code in half. Really strange. In On Duty I use streaming PCM directly from cart. That is also possible if you can guarantee that nobody needs cart access.
  • Recently Browsing   0 members

    • No registered users viewing this page.

  • Create New...