Jump to content
Sign in to follow this  
Lillapojkenpåön

My assembly questions thread

Recommended Posts

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?

 

PoliticalHarmoniousHarborseal-size_restr

 

 

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 by Lillapojkenpåön

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
Thanks for the help ZackAttack and AkashicRecord!



WelldocumentedOfficialEmperorpenguin-siz

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 by Lillapojkenpåön

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

 

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 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
; 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?

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites


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)

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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? :)

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

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 by Nukey Shay

Share this post


Link to post
Share on other sites

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.

  • Like 2

Share this post


Link to post
Share on other sites

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 by Lillapojkenpåön

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...