In the previous post, the code I wrote worked fine but was not optimal. Christopher Tumber pointed out the "trf a,b" instruction was redundant at the beginning of the code. Also there was some duplication of code when storing some of the digits, this would make the code larger than it needs to be. So I've been back over the code and fixed it.
Edit:
Also Gauze mentioned a mistake, where I used $80 (which is a memory location rather than a value) as the terminator for the string - it should be #$80 !
Vectrex Programming
Wednesday, 26 November 2014
Tuesday, 25 November 2014
Converting a byte value to ascii in 6809
This is less of a tutorial but more a commented snippet of code, When I find more time I will package this up into a zip with some supporting code that will run it on the Vectrex. Please leave any questions, suggestions etc. in the comments or on the vector gaming forum.
Sometimes you want to print a value out in your game, the problem with assembly is that if you decide to print something midway through some other code a you are potentially trashing what you are in the middle of doing.
You could get around this by saving all your registers to the stack, or just print what is in memory later on when it's safe to do so.
First we need a routine to convert our number from binary to decimal.
So in a modern language you might write:
A=123;
print(A);
Output:
123
Easy stuff!
On the Vectrex unless there is a routine already coded to do this in the bios you are going to have to write your own! The first thing we need to think about is what type of decimal value are we going to convert to. A single byte could represent 0 to 255, -128 to 127 or even a fixed point vale. Not to complicate things we are going to write a routine for twos compliment -128 to 127. So here is some commented code that converts whatever is in the A register to ASCII including the sign (+ or -) and a terminator. The terminator tells the Vectrex bios print routine that it should stop printing (otherwise it would keep printing garbage across your screen!).
Sometimes you want to print a value out in your game, the problem with assembly is that if you decide to print something midway through some other code a you are potentially trashing what you are in the middle of doing.
You could get around this by saving all your registers to the stack, or just print what is in memory later on when it's safe to do so.
First we need a routine to convert our number from binary to decimal.
So in a modern language you might write:
A=123;
print(A);
Output:
123
Easy stuff!
On the Vectrex unless there is a routine already coded to do this in the bios you are going to have to write your own! The first thing we need to think about is what type of decimal value are we going to convert to. A single byte could represent 0 to 255, -128 to 127 or even a fixed point vale. Not to complicate things we are going to write a routine for twos compliment -128 to 127. So here is some commented code that converts whatever is in the A register to ASCII including the sign (+ or -) and a terminator. The terminator tells the Vectrex bios print routine that it should stop printing (otherwise it would keep printing garbage across your screen!).
; Convert 8bit value to ASCII
;Requires 5 bytes of ram to save each digit to
;Debug_0 (sign)
;Debug_1 (Digit 1)
;Debug_2 (Digit 2)
;Debug_3 (Digit 3)
;Debug_4 (string terminator)
;Convert Register A to a 3 byte Decimal for printing to screen
Reg_To_Decimal:
tfr a,b ;transfer a copy of a to b
tstb ;test a to check if neg
bpl Reg_DA ;branch if plus (positive)
nega ;else is negative so negate a to be positive
ldb #45 ;"-" sign
stb Debug_0
bra Reg_D0
Reg_DA:
ldb #43 ;"+" sign
stb Debug_0
Reg_D0:
tfr a,b
suba #100
bge Reg_D1 ;if 0 or Greater first digit = 1
lda #48 ;else it = 0 = (ascii 48)
sta Debug_1
bra Reg_D2
Reg_D1:
tfr a,b ;copy remaining number after subtraction of 100 to b
lda #49 ;set digit to 1+ascii (ascii 49)
sta Debug_1
Reg_D2:
clra ;clear a is the counter
Reg_D2_Loop: ;Div10
inca ;this might mean our count is 1 more than it should be
subb #10 ;take away 10 until it is less than or equal to 0
bge Reg_D2_Loop ;if b is 0 or greater
adda #47 ;Result is 1 higher than required so add ascii 48-1
sta Debug_2 ;a count is the middle digit
;b must now contain the final digit but it will be negative, we need to add 10 to b + ascii
addb #58 ;add 10+ascii
stb Debug_3
lda $80 ;string terminator
sta Debug_4
rts
Sunday, 11 May 2014
Tutorial 1: Tools and Tech
Tutorial 1: Tools and Tech
Introduction
This is the first of a step by step guide to programming the vectrex using asm (assembly). It's recommended that you have some experience in programming another language and understand concepts such as flow control and what bits and bytes are. I would first recommend you look at the excellent resources over at www.playvectrex.com:http://www.playvectrex.com/designit_f.htm
It is very important you read these two tutorials (in this order):
This one by Chris Tumber is a great 6809 asm primer
http://www.playvectrex.com/designit/christumber/tutorial.htm
Also Christopher Salomons which focuses on vectrex specifics
http://www.playvectrex.com/designit/chrissalo/toc.htm
Both tutorials contain example programs "Your First Program" and "Hello World" respectively. Chris Salomons is maybe a bit advanced for some beginners. Take as much as you can in.
We will also be working towards creating a "Hello World" but also a basic framework so you can have a template to reuse every time you start a new project.
Tools
Text editor: Notepad++ (NPP)Assembler: AS09 1.42 (by Frank Kingswood)
Emulator: ParaJVE
Optional: NPP 6809/Vectrex highlighting
You don't have to use Notepad++ (NPP) , you could use notepad or any other text editor you prefer. My preference is NPP because of its many features and user defined syntax highlighting.
To make things easier I've packaged the assembler with some files to get you going.
Framework_tutorial_1.0 (includes AS09 1.42)
Extract the tutorial to a folder, I tend to keep each project in its own folder and include a copy of the AS09 executable with each project.
To compile the project, just run the framework.bat batch file and it should output a .bin file which can be loaded into the emulator as a ROM, if something goes wrong a file called error will appear. This error file is a text file so you can open it in NPP and keep it open in another tab while you program and check it if something goes wrong. Hopefully you shouldn't get any error file yet!
Now open the framework.asm file in NPP and you will see the code,this version is heavily commented. If you want it to look as "pretty" as below you will need to install the NPP user defined language file (NPP 6809/Vectrex highlighting) link above.
For now I'm not going to break down every line of code, but just rely on the comments in the asm file.
Have a read of this code and its comments, run the framework.bat, load the bin file it makes into the emulator as a ROM and you should see hello world appear on the screen. If you press a button, it should disappear and only show a black screen. You can reload the ROM again in ParaJVE from the debug menu...
TASK 1:
try changing the "HELLO WORLD" text to something else and save the ASM file, run the batch file to make the bin, reload the rom from the debug menu in ParaJVE. Does it still work?
framework.asm
; *** Framework to get a project going
; Mikiex 2014
; This assumes you have read the tutorial by Chris Tumber and Christopher Salomon and taken in as much as possible
;
;notice the include is indented this is very important, your code won't work with you don't use indents correctly
;The assembler expects certain things and indented or not is one of those things.
include "VECTREX.I" ; This is an include file, when you run the assembler, Imagine it is as if the contents of the .I file
; is pasted into this asm file here.
; VECTREX.I contains labels and what they equal, labels are just text that mean a memory location
; whether it be in RAM or ROM. We don't have to use them but its just much easier to remember:
; jsr Reset0Ref (jump to subroutine Reset0Ref)
; means the same as:
; jsr $F354 (jump to subroutine at memory $F354)
; Look inside the VECTREX.I file and you will see the line:
; Reset0Ref equ $F354
; This is telling the assembler this label means this number
; The name used for the label could be anything as long as you don't duplicate them
; Instead of Reset0Ref you could have RedApples but it would be less meaningful!
; But for consistency I would recommend using this VECTREX.I , it will mean all your
; projects
; Also for the bios source code uses the same label names
; http://www.playvectrex.com/designit/chrissalo/bios.htm
; Its a good reference and explains what the bios routines do
;Macros
;This is just a place where we might put macros, a more advanced topic! It might be a better idea to put them in another
;include file. Another reason to use include files are a nice way to hide away stuff, keep things neater!
;Variables These labels point to ram locations that we will use to store things that can be changed by our code
;again we don't have to use labels, but its going to make life easier!
;A label only points to a single byte of memory, but sometimes you need 2 bytes or more
;In the case of 2 bytes you just need to leave a byte free before you put your next variable.
;A common way to assign memory is shown below, you take the previous label and add +1 to it
;This will give the next byte in memory
;again notice these lines are not indented, that's because they start with labels!
y_position equ $c880 ;y Position of text ($c880 is the first bit of ram the user is safe to use)
x_position equ y_position+1 ;x Position of text (y_position = $c880, so to the assembler y_position+1 = $c881)
dead_flag equ x_position+1 ;dead flag, 0=alive 1=dead
somebytes equ dead_flag+1 ;this value will be 2 bytes, this label points at the 1st byte..we just imagine the next byte
anotherbyte equ somebytes+2 ;this is 1 byte but we use 2+ more than the previous label to leave a gap behind it
nextbyte equ anotherbyte+1 ;I hope people get this because its hard to explain!
;The last 3 labels above are just examples that are not used by the actual code anywhere..
;note there are only 874 bytes of "safe" RAM available to the user!!
;There is more RAM, but when I say safe - its RAM not used by the bios, its possible to "borrow" ram thats used for other things
;but lets not get into that! Just remember you have plenty of ROM and hardly any RAM!
;Constants Constants are things that never change, they are labels that replace numbers in code
;They are a handy shortcuts that the assembler converts to numbers (really they are just the same as other labels)
;You can even do maths with them, but this maths happens when the file is assembled
;not on the vectrex !
;It's a common convention to use ALL CAPITAL letters with constants
;just a handy reminder that these values are constant and do not change when the program is running.
;again these are labels so no indenting!
YPOS equ 50 ; Y position of hello world
XPOS equ -40 ; X position of hello world
;VERY IMPORTANT
;NOTICE all instructions such as equ,org,fcb,jsr,lda must all be lowercase to not cause errors with AS09 1.4
org 0 ;This tells the assembler everything below this is in ROM (org is short for origin) 0 is the
;first memory location on the rom
;note again it is important this is indented!
; Below is the "magic" init block, all vectrex programs start with it, you can change some bits of it
; You don't need to worry much about it, just know that it needs to be there and be valid.
; *** Init block
fcb $67,$20
fcc "GCE 2014" ;you can change this, but test it - CAPITALS ONLY!
fcb $80
fdb musa ;This points to music (bottom of this file), needed even if there is no music
fdb $f850
fdb $30b8
fcc "FRAMEWORK" ;This is the Title, change it to what you want - CAPITALS ONLY!
fcb $80,$0
; *** Start Code
; This is the start of our code!!!
;here I am using a colon after the label, you don't have to and the assembler probably removes it.
;but I like to used one just to show this label is used to jump to this point in code
;you can use these as places to jump to.
;note you don't include the colon when using the label is used after an instruction eg. jsr game_loop
;does not have a colon at the end of it!
;mine are all lower-case, but you could mix case
boot_init:
;boot initialise, anything we need to do only when loading the game for the first time would go here
;initialise some variables call this after boot and when game ends
init:
clra ;clear the A register, this sets the A register to 0, faster than using lda #0
sta dead_flag ;store the value in A to this memory location (dead_flag)
lda #10 ;load the A register with the decimal value 10, can be between -128 and 127 (twos complement format)
sta y_position ;store the value in A (which is 10)
sta x_position ;again store what is in a (10)
jsr Read_Btns ;get the state of buttons for comparison to when we do want to check them (sort of like calibrating joystick)
;at the end of this init, the code will just contine onto startscreen
startscreen:
jsr Wait_Recal ; BIOS recalibration of the screen, this must be called every frame!
jsr print_hello ;jsr means jump to subroutine, in this case jump to print_hello, once the subroutine returns (rts instruction)
;it will return back to this point in the code
jsr Read_Btns ;read the buttons, when this routine returns it puts the results in A
cmpa #0 ;compare if A is 0 , we use the result in the next instruction...
beq startscreen ;no button pressed, beq (branch equal), so if the result of the compare above is 0 goto startscreen
;if it was not equal to 0 then something was pressed (in this case any of the buttons!)
;so the code will just carry on into the game_loop below...
game_loop: ;This is our game loop NOTHING HAPPENS IN IT FOR NOW JUST A BLACK SCREEN
jsr Wait_Recal ; BIOS recalibration, now we are in a new loop we have to call this once a frame
lda dead_flag ;load A with value in dead_flag
cmpa #1 ;compare A with 1
beq init ;if the previous instruction was equal branch to init (that would mean player is dead) so goto init label
bra game_loop ;bra (branch always) so no matter what it will always branch to game_loop if it gets here!
print_hello:
jsr Reset0Ref ; sets the pen back to 0,0 centre of the screen
lda #100 ; load A with 100
jsr Intensity_a ; Sets the intensity to A (so in this case 100)
lda #-6 ; load A with Value to be used for height of text
ldb #40 ; load B with Value for the width of text
sta Vec_Text_Height ; Store A to the ram location the bios routine uses for height of text
stb Vec_Text_Width ; same with B
ldu #hello_string ; label of string (# in front means the memory location of the label rather than value in it)
lda #YPOS ; Y position of text
ldb #XPOS ; X position of text
jsr Print_Str_d ; Vectrex BIOS print routine - now everything is in place go print it!
rts ; returns back from this subroutine (in this case back to the line after jsr print_hello)
; Generally you want to keep all your data at the end of the rom
; after all your code, its better because your code is less likely to need long branching which costs more.
; So in this example we are storing a string for hello world
hello_string:
db "HELLO WORLD" ; only capital letters
db $80 ; $80 is end of string it lets the print routine know the text ends here
musa ; Start music that plays nothing
fdb $fee8
fdb $fbe6
fcb $0,$80
fcb $0,$80
TASK 2:
Try changing the position and size of the text.
(Note y_position and x_position are not used yet, we will use them in another tutorial)
Next time we will go into more detail, hopefully people can give feedback in the comments and also on the vector gaming forum here:
http://vectorgaming.proboards.com/thread/992/vectrex-programming-tutorial-tools-tech
Subscribe to:
Posts (Atom)