Lillapojkenpåön Posted October 30, 2018 Share Posted October 30, 2018 (edited) I'm trying to get used to assembly by doing some cool things I couldn't do in bB like repositioning the ball in this case I'm shure someone can spot what's wrong right away A. Why is the 160 to 0 transition so unsmooth? B. Why does the second instance of the ball show up so late horisontally? (it also starts at 0) That would probably solve the jitter to C. Am I uing hmclr right? do I even need to use it? Main: jsr VerticalSync ; Jump to SubRoutine VerticalSync jsr VerticalBlank ; Jump to SubRoutine VerticalBlank jsr Kernel ; Jump to SubRoutine Kernel jsr OverScan ; Jump to SubRoutine OverScan jmp Main ; JuMP to Main VerticalSync: lda #2 ; LoaD Accumulator with 2 so D1=1 ldx #49 ; LoaD X with 49 sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) sta VSYNC ; Accumulator D1=1, turns on Vertical Sync signal stx TIM64T ; set timer to go off in 41 scanlines (49 * 64) / 76 sta WSYNC ; Wait for Sync - halts CPU until end of 1st scanline of VSYNC sta WSYNC ; wait until end of 2nd scanline of VSYNC lda #0 ; LoaD Accumulator with 0 so D1=0 sta WSYNC ; wait until end of 3rd scanline of VSYNC sta VSYNC ; Accumulator D1=0, turns off Vertical Sync signal rts ; ReTurn from Subroutine VerticalBlank: lda ballx sta HMCLR jsr PosObject sta HMBL ; 5 19 - store fine tuning of X ;sta.wx HMBL ; 5 19 - store fine tuning of X sta RESBL ; 4 23 - set coarse X position of object sta WSYNC sta HMOVE lda $00 sta CTRLPF rts ; ReTurn from Subroutine ;The Kernel is the section of code that draws the screen. Kernel: sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) lda INTIM ; check the timer bne Kernel ; Branch if its Not Equal to 0 ; turn on the display sta VBLANK ; Accumulator D1=0, turns off Vertical Blank signal (image output on) ldx #0 ; Load X with 192 stx temp2 lda bally adc ballheight sta temp3 lda missile0y adc missile0height sta temp4 ;sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) ; draw the screen KernelLoop: ldy #1 ; 2 2 - D1=0, so ball will be off LDA $0E sta COLUBK ; STore X into TIA's background color register LDA temp2 ;enables ball if line counter is bigger then bally and smaller then bally+ballheight cmp bally ; bcc skipEnabl ; cmp temp3 bcc DoEnabl ;falls through when first instance of ball has been drawn sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) ;updates ballx and finepos on line 30 cmp #30 bne skiprepos ;inc linecounter two times because two WSYNC below ; INC temp2 INC temp2 lda missile0x ; second ballx sta HMCLR jsr PosObject sta HMBL ; 5 19 - store fine tuning of X ;sta.wx HMBL ; 5 19 - store fine tuning of X sta RESBL ; 4 23 - set coarse X position of object sta WSYNC sta HMOVE jmp skiprepos ; 3 14 - $24 = BIT with zero page addressing, trick that DoEnabl: ; 12 - from bcs DoEnablPre iny ; 2 14 - D1=1, so ball will be on skipEnabl sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) skiprepos INC temp2 sty ENABL ; 3 17 ;enables ball second time if line counter is bigger then missile0y and smaller then missile0y+missile0height LDA temp2 cmp missile0y ; bcc skipEnabl2 ; cmp temp4 bcc DoEnabl2 ;falls through when second instance of ball has been drawn jmp skipEnabl2 DoEnabl2: lda $21 sta CTRLPF iny ; 2 14 - D1=1, so ball will be on skipEnabl2 sty ENABL ; 3 17 ;LDA temp2 CMP #191 bcc KernelLoop rts ; ReTurn from Subroutine PosObject sec sta WSYNC DivideLoop88 sbc #15 ; 2 2 - each time thru this loop takes 5 cycles, which is bcs DivideLoop88 ; 2 4 - the same amount of time it takes to draw 15 pixels eor #7 ; 2 6 - The EOR & ASL statements convert the remainder asl ; 2 8 - of position/15 to the value needed to fine tune asl ; 2 10 - the X position asl ; 2 12 asl ; 2 14 rts ; 6 29 OverScan: sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) lda #2 ; LoaD Accumulator with 2 so D1=1 sta VBLANK ; STore Accumulator to VBLANK, D1=1 turns image output off lda #32 ; set timer for 27 scanlines, 32 = ((27 * 76) / 64) sta TIM64T ; set timer to go off in 27 scanlines ; game logic will go here ;dec bally inc ballx inc missile0x inc missile0x lda ballx ;inc SpriteXPosition; increment the desired position by 1 pixel ; ldx SpriteXPosition cmp #160 ; has it reached 160? bcc LT1606 ; this is equivalent to branch if less than lda #0 ; otherwise reload with 0 sta ballx LT1606 lda missile0x ;inc SpriteXPosition; increment the desired position by 1 pixel ; ldx SpriteXPosition cmp #160 ; has it reached 160? bcc LT16066 ; this is equivalent to branch if less than lda #0 ; otherwise reload with 0 sta missile0x LT16066 OSwait: sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) lda INTIM ; Check the timer bne OSwait ; Branch if its Not Equal to 0 rts ; ReTurn from Subroutine Edited October 30, 2018 by Lillapojkenpåön Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted October 31, 2018 Author Share Posted October 31, 2018 I got a stable display by moving some stuff back to PosObject but the other little things remain I did this in bB but if you would like to compile then define these variables.. bally ballx ballheight missile0y missile0x missile0height temp2 temp3 temp4 lda #10 sta bally sta ballx sta ballheight lda #50 sta missile0y sta missile0x sta missile0height Main: jsr VerticalSync ; Jump to SubRoutine VerticalSync jsr VerticalBlank ; Jump to SubRoutine VerticalBlank jsr Kernel ; Jump to SubRoutine Kernel jsr OverScan ; Jump to SubRoutine OverScan jmp Main ; JuMP to Main VerticalSync: lda #2 ; LoaD Accumulator with 2 so D1=1 ldx #49 ; LoaD X with 49 sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) sta VSYNC ; Accumulator D1=1, turns on Vertical Sync signal stx TIM64T ; set timer to go off in 41 scanlines (49 * 64) / 76 sta WSYNC ; Wait for Sync - halts CPU until end of 1st scanline of VSYNC sta WSYNC ; wait until end of 2nd scanline of VSYNC lda #0 ; LoaD Accumulator with 0 so D1=0 sta WSYNC ; wait until end of 3rd scanline of VSYNC sta VSYNC ; Accumulator D1=0, turns off Vertical Sync signal rts ; ReTurn from Subroutine VerticalBlank: lda ballx ;sta HMCLR jsr PosObject lda $00 sta CTRLPF rts ; ReTurn from Subroutine ;The Kernel is the section of code that draws the screen. Kernel: sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) lda INTIM ; check the timer bne Kernel ; Branch if its Not Equal to 0 ; turn on the display sta VBLANK ; Accumulator D1=0, turns off Vertical Blank signal (image output on) ldx #0 ; Load X with 192 stx temp2 lda bally adc ballheight sta temp3 lda missile0y adc missile0height sta temp4 ;sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) ; draw the screen KernelLoop: ldy #1 ; 2 2 - D1=0, so ball will be off LDA $0E sta COLUBK ; STore X into TIA's background color register LDA temp2 ;cmp #31 ;bcs skipEnabl ;enables ball if line counter is bigger then bally and smaller then bally+ballheight cmp bally ; bcc skipEnabl ; cmp temp3 bcc DoEnabl ;falls through when first instance of ball has been drawn ;sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) ;updates ballx and finepos on line 30 cmp #30 bne skipEnabl ;inc linecounter two times because two WSYNC below ; INC temp2 INC temp2 lda missile0x ; second ballx ;sta HMCLR jsr PosObject jmp skiprepos ; DoEnabl: ; 12 - from bcs DoEnablPre iny ; 2 14 - D1=1, so ball will be on skipEnabl sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) skiprepos INC temp2 sty ENABL ; 3 17 ;enables ball second time if line counter is bigger then missile0y and smaller then missile0y+missile0height LDA temp2 cmp missile0y ; bcc skipEnabl2 ; cmp temp4 bcc DoEnabl2 ;falls through when second instance of ball has been drawn jmp skipEnabl2 DoEnabl2: lda $21 sta CTRLPF iny ; 2 14 - D1=1, so ball will be on skipEnabl2 sty ENABL ; 3 17 ;LDA temp2 CMP #192 bcc KernelLoop rts ; ReTurn from Subroutine PosObject sec sta WSYNC DivideLoop88 sbc #15 ; 2 2 - each time thru this loop takes 5 cycles, which is bcs DivideLoop88 ; 2 4 - the same amount of time it takes to draw 15 pixels eor #7 ; 2 6 - The EOR & ASL statements convert the remainder asl ; 2 8 - of position/15 to the value needed to fine tune asl ; 2 10 - the X position asl ; 2 12 asl ; 2 14 sta HMBL ; 5 19 - store fine tuning of X ;sta.wx HMBL ; 5 19 - store fine tuning of X sta RESBL ; 4 23 - set coarse X position of object sta WSYNC sta HMOVE rts ; 6 29 OverScan: sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) lda #2 ; LoaD Accumulator with 2 so D1=1 sta VBLANK ; STore Accumulator to VBLANK, D1=1 turns image output off lda #32 ; set timer for 27 scanlines, 32 = ((27 * 76) / 64) sta TIM64T ; set timer to go off in 27 scanlines ; game logic will go here ;dec bally inc ballx inc missile0x inc missile0x lda ballx ;inc SpriteXPosition; increment the desired position by 1 pixel ; ldx SpriteXPosition cmp #160 ; has it reached 160? bcc LT1606 ; this is equivalent to branch if less than lda #0 ; otherwise reload with 0 sta ballx LT1606 lda missile0x ;inc SpriteXPosition; increment the desired position by 1 pixel ; ldx SpriteXPosition cmp #160 ; has it reached 160? bcc LT16066 ; this is equivalent to branch if less than lda #0 ; otherwise reload with 0 sta missile0x LT16066 OSwait: sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) lda INTIM ; Check the timer bne OSwait ; Branch if its Not Equal to 0 rts ; ReTurn from Subroutine Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted November 5, 2018 Author Share Posted November 5, 2018 I would really appreciate some help, I can get the second ball to show up on the left and dissapear on the right side instead, or show up on both sides and dissapear in the middle, depending on where I place the wsync, but why? Quote Link to comment Share on other sites More sharing options...
ZackAttack Posted November 6, 2018 Share Posted November 6, 2018 So one issue is that you modified the PosObject Routine: sta HMBL ; 5 19 - store fine tuning of X ;sta.wx HMBL ; 5 19 - store fine tuning of X That sta is only 3 cycles, so the strobe happens 6 pixels early now. Regarding HMCLR, you only need to use that if you need to prevent objects from moving during extra HMOVEs. If you're always calling PosObject for every object and only strobing HMOVE once you don't need to bother with clearing. DON"T FORGET not to strobe HMCLR immediately after a HMOVE. You need to wait at least 23 cycles, but most just hit an extra WSYNC before HMCLR. I think this was one of the problems in the first version you posted. Another issue that you may run into deals with the "bcs DivideLoop88" in the PosObject routine. If that portion of the routine happens to span multiple pages it will cause the branch to take an extra cycle and completely mess up all positioning. It's unlikely to happen since it's only a few bytes of code, but it has happened to me before. Either use ALIGN or put it at $1000 to be sure that doesn't happen. Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted November 27, 2018 Author Share Posted November 27, 2018 (edited) Thanks for the help ZackAttack and AkashicRecord! The stars are one missile and the ship is the other missile and you can't tell but there's no hmove bars.. I tried to do the positioning in overscan, store the result in variables and just load them during the repos in the kernel to keep it one line only, I didn't get it to work, but I also realised right now that I probably stored the finepos back to the integer variable so better try again with different dedicated variables.. Is that possible? Repositioning that takes less then two lines? Edited November 27, 2018 by Lillapojkenpåön Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted December 19, 2018 Author Share Posted December 19, 2018 If I have a big chunk of code that I want to re-use for eight virtual sprites using index registers, is that possible if the code contains bit operations? Like instead of doing this LDA NUSIZ4 ORA #64 STA NUSIZ4 doing something like this? LDX #0 LDA _byte,x ORA _bit,x STA _byte,x _byte .byte NUSIZ4 _bit .byte #64 Quote Link to comment Share on other sites More sharing options...
ZackAttack Posted December 20, 2018 Share Posted December 20, 2018 If I have a big chunk of code that I want to re-use for eight virtual sprites using index registers, is that possible if the code contains bit operations? Like instead of doing this LDA NUSIZ4 ORA #64 STA NUSIZ4 doing something like this? LDX #0 LDA _byte,x ORA _bit,x STA _byte,x _byte .byte NUSIZ4 _bit .byte #64 If you intend on ORing 64 with each of them you would want to use the immediate mode for the ORA instruction. As far as indexing the different virtual sprites, that would depend on how they are laid out in memory. If they are all sequential in a chuck of contiguous memory there is no need for _byte. There are several ways to go about this, but I'd expect it to look something like this: SEG.U VARS ORG $80 NUSIZ_ARRAY ; Multiple labels at same address since both the array and NUSIZ0 are located starting here NUSIZ0 ds 1 NUSIZ1 ds 1 NUSIZ2 ds 1 ; Set bit $20 for NUSIZ0-NUSIZ2 LDX 2 ; start with NUSIZ2 and work back to 0 Loop: LDA NUSIZ_ARRAY,x ORA #64 STA NUSIZ_ARRAY,x DEX BPL Loop Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted December 21, 2018 Author Share Posted December 21, 2018 Thank you, that worked, but is there a way to load and store the value of bytes from a table like i tried? if I run into a situation that might require that? Quote Link to comment Share on other sites More sharing options...
ZackAttack Posted December 21, 2018 Share Posted December 21, 2018 Sure. It's probably better to ask a more specific question when you get there though. The general idea would be to have a label followed by some ROM data and then use LABEL,X as the addressing mode. If you need another level of indirection it may be necessary to go with (),y. In which case you'd have multiple arrays in ROM and a 2 byte pointer variable in RAM. You'd copy the pointer to RAM for the desired array before the loop and then use (POINTER_VAR),Y inside the loop. Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted December 30, 2018 Author Share Posted December 30, 2018 I don't understand it correctly I think, can you give an example? I want to have different ram adresses in the table (that are not consecutive) and load the value of that adress I want to make this re-usable LDA P1_Counter ; P1_Counter is a ram byte this compiles but I think it loads the ram adress instead of the value of that ram adress? It doesn't work as the code above atleast LDY #0 LDA _player_counter,y _player_counter .byte P1_Counter, P2_Counter....etc. if I try the other adressing mode I get Value in 'lda (_player_counter),y' must be <$100 Quote Link to comment Share on other sites More sharing options...
ZackAttack Posted December 31, 2018 Share Posted December 31, 2018 You can find a fully working example of this in the Collect Tutorial. A 2 byte variable is created to hold the pointer to the human graphics. ORG $80 HumanPtr: ds 2 ; used for drawing player0 The address of the graphics to point to is calculated and stored in HumanPtr. Note that since this is a 2 byte pointer variable the low byte is stored at HumanPtr and the high byte is stored at HumanPtr+1. ; HumanPtr = HumanGfx + HUMAN_HEIGHT - 1 - Y position lda #<(HumanGfx + HUMAN_HEIGHT - 1) sec sbc ObjectY sta HumanPtr lda #>(HumanGfx + HUMAN_HEIGHT - 1) sbc #0 sta HumanPtr+1 Indirect Y addressing is used to load a byte from the array stored at the location pointed to by HumanPtr with the index/offset specified by Y. lda (HumanPtr),y ; 5 28 - load the shape for player0 Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted January 5, 2019 Author Share Posted January 5, 2019 ; POINTER_VAR = VAR_TABLE + TABLE_HEIGHT - 1 lda #<(VAR_TABLE + TABLE_HEIGHT - 1) ;sec ;sbc ObjectY sta POINTER_VAR lda #>(VAR_TABLE + TABLE_HEIGHT - 1) sbc #0 sta POINTER_VAR+1 ldy #3 lda (POINTER_VAR),y VAR_TABLE: .byte NUSIZ9 .byte m .byte m .byte f .byte a .byte NUSIZ8 .byte l .byte g .byte h .byte o TABLE_HEIGHT = * - VAR_TABLE Would that load the value of the f variable? Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted January 6, 2019 Author Share Posted January 6, 2019 I understand that that's not right but I have to ask stupid questions until I connect the dots Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted January 6, 2019 Share Posted January 6, 2019 In the assembly file, variable f is the ram address where its value is stored. i.e. If your program assigned the first byte of user ram $80 to this variable, assembling the program will write $80 to the binary wherever it finds variable f. To get a value from a variable culled from a list of variables (as shown above), this adds an extra step. So you'd need something like this: ldy #3 lda (POINTER_VAR),y tax ; transfer ram address to a pointer lda $00,x ;get f's current value The Adventure source does this all over the place. Quote Link to comment Share on other sites More sharing options...
bogax Posted January 6, 2019 Share Posted January 6, 2019 I'd encourage you to make the locations consecutive and treat them as a table if possible If they need to be random and you're going to address them indirectly through a table then you don't need to use the lda (address),y mode unless you're going to change tables ie unless address is going to change (if the value in memory at location "address" needs to be changeable, that is the table location needs to be a variable) POINTER_VAR = VAR_TABLE + TABLE_HEIGHT - 1 has (POINTER_VAR),0 (ie y = 0) pointing at o the last byte of the VAR_TABLE table. (VAR_TABLE),3 (y = 3) isn't in the table re Nukey Shay's example if the table isn't going to change then you can load x (more) directly ldy #$03 ldx VAR_TABLE,y lda $00,x as Nukey Shay said "f" is an alias for a number in bB you'd do const f = $d6 $d6 is the location in memory of bB variable f (but of course it's predefined in bB in 2600basic.h) Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted January 6, 2019 Author Share Posted January 6, 2019 I'd encourage you to make the locations consecutive and treat them as a table if possible I have allready done that, but still want to learn, All different examples are usefull for different things, my first idea of how it could be done wasn't far off, essentially the same. ldy #03 ldx VAR_TABLE,y lda a,x VAR_TABLE .byte #00 ;a .byte #02 ;c .byte #06 ;g .byte #05 ;f but your version is easier ldy #$03 ldx VAR_TABLE,y lda $00,x VAR_TABLE .byte a .byte c .byte g .byte f Thanks alot for the help! Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted January 28, 2019 Author Share Posted January 28, 2019 Is it possible to somehow load different opcodes? If I sometimes want to AND and sometimes ORA for example. Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted January 28, 2019 Share Posted January 28, 2019 not sure what you're after, but use branches to control program flow. USE_AND is a value in RAM that controls the flow, only bit 7 is used so the other bits are free to use for other things. BIT USE_AND ; bit 7 of USE_AND ends up in the sign flag BMI .skip_ora ; if bit 7 is on then branch to .skip_ora ORA #VALUE ; bit 7 was off, so use ORA .BYTE $0C ; trick that skips over AND #VALUE. ;$0C = NOP $absolute - this "sucks up" the AND #VALUE by treating it as an absolute address. . skip_ora AND #VALUE ; bit 7 was on, so use AND ...continue Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted January 29, 2019 Author Share Posted January 29, 2019 That was what I was after Just remember hearing David Crane talking about jumping to tables that ended in an opcode or something like that to save rom.. I don't know what I was thinking? Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted January 30, 2019 Author Share Posted January 30, 2019 How come this doesn't work? It's suppose to turn on a bit if you score a hundred points, and if the bit is on the next frame it doesn't do the check, but it just keeps scoring 100 for some players and doesn't even do the first check for others, I've set the d byte to zero beforehand and it worked perfect before I tried it with the BIT table? BIT .byte #2 .byte #4 .byte #8 .byte #16 .byte #32 .byte #64 .byte #128 .byte #1 ldx #7 _loop BIT d AND BIT,x ; check if restrainer is turned on BNE .____skip LDA player1y,x CMP #180 BCS .____skip LDA player1x,x CLC ADC #8 CMP missile0x BCC .____skip LDA player1x,x CMP temp2 BCS .____skip LDA d ORA BIT,x ; turn on restrainer so you don't keep scoring 100 every frame STA d SED CLC LDA score+1 ADC #$01 STA score+1 LDA score ADC #$00 STA score CLD .____skip dex bpl _loop Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted January 30, 2019 Share Posted January 30, 2019 (edited) The very first line of the loop is BIT d instead of LDA d. Remember...the BIT opcode does not alter the accumulator, only status flags. So the subsequent AND is acting on whatever currently IS in the accumulator...which most-likely isn't d. BNE then branches according to that test. BTW it's not good practice to name a label the same as an opcode (or anything else used by your program, for that matter). It will make reading/debugging a lot more confusing down the line. You could use BITtable or whatever instead to keep it unique. Edited January 30, 2019 by Nukey Shay Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted January 30, 2019 Share Posted January 30, 2019 Is it possible to somehow load different opcodes? If I sometimes want to AND and sometimes ORA for example. If you just want to flip a bit if something had occurred, that is what EOR is for. "No-body likes me" Cut it out EOR. We know that you are there when we need you. 2 Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted January 30, 2019 Author Share Posted January 30, 2019 Thank you! Quote Link to comment Share on other sites More sharing options...
Lillapojkenpåön Posted February 12, 2019 Author Share Posted February 12, 2019 (edited) Is it possible to have a table of tables? So that when the current table index reaches 256 you increment another variable that you load as the index of what table to read from, so now it starts at the next table in the table? I'm trying to make playfield scrolling as close to endless as possible without alot of extra logic Edited February 12, 2019 by Lillapojkenpåön Quote Link to comment Share on other sites More sharing options...
+Karl G Posted February 12, 2019 Share Posted February 12, 2019 Once you reach the end of the table, if you are reading from a pointer, you could incremens the high bit of the pointer to be able to read from the next page of data. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.