Programming for the Absolute Beginner 2nd Edition by Jerry Ford
I was excited to see that a second edition of Jerry Ford's programming book was recently released. It uses Just BASIC (all examples also compatible with Liberty BASIC) to teach the fundamentals of programming.
Jump to Amazon's link
Here is the table of contents. :-)
1 Introduction to Programming
2 Creating Programs with Just BASIC
3 Creating Graphical User Interfaces
4 Working with Variables and Arrays
5 Making Decisions with Conditional Logic
6 Using Loops to Process Data
7 Improving Program Organization with Functions and Subroutines
8 Working with Text Files
9 Working with Sound and Graphics
10 Arcade-Style Computer Game Development
11 Debugging Your Applications
Monday, August 22, 2016
Friday, August 19, 2016
Graphing Data - Setting the stage
If I have a set of data for sales, or temperature, or mosquito populations, or anything at all and I want to plot it on an X/Y graph, what are my choices for layout?
Should our graphing library figure out what the ranges are and set its own scaling? Should it require that we tell it up front what the X and Y scales are? Maybe it can even be designed to allow for changing the scale on the fly?
It would be great if our data plotting graphics library could handle plotting to screen, saving that to a file as a bitmap, and also plotting to a printer.
It would be great if our data plotting graphics library could handle plotting to screen, saving that to a file as a bitmap, and also plotting to a printer.
How about data sources and formats? Should we design it to accept one or more strings of delimited values? In the graphing example at runbasic.com it allows you to upload a CSV file with data to plot. Maybe as an interesting example I should find a downloadable file from a scientific website and the project should be to plot a graph from that?
Feedback welcome. More to come.
Feedback welcome. More to come.
Labels:
bitmap,
climate data,
comma delimited,
csv,
data format,
format,
graphing,
layout,
library,
plotting,
printer,
printing,
sales,
scientific data
Monday, August 08, 2016
Graphing data
I get a lot of questions about how to draw graphs involving progressions of numbers, and comparisons of trends, etc. Liberty BASIC doesn't have a built in graphing function, but it does support rich graphics drawing.
So if you're looking for a way to draw graphs of money, web traffic, rainfall, or whatever else stay tuned over the next couple of weeks. We're going to blaze a trail!
There are at least a couple of options for solving this problem.
So if you're looking for a way to draw graphs of money, web traffic, rainfall, or whatever else stay tuned over the next couple of weeks. We're going to blaze a trail!
There are at least a couple of options for solving this problem.
- Export data out to a CSV file and use Excel to plot graphs
- Use Liberty BASIC's graphics drawing features and draw the graphs ourselves
We are going to draw the graphs ourselves.
Impatient for the next post? Check out this link!
Stay tuned!
Labels:
bar graphs,
basic programming,
cvs,
drawing,
excel,
graphics,
graphing,
liberty basic,
line graphs,
microsoft excel,
qbasic
Tuesday, August 02, 2016
Blast from the past - Whatever happened to PEEK and POKE?
I am reposting the following blog entry from ten years ago because it has been one of the most popular posts ever according to Blogger's statistics graph. The link at the end of the post was broken, so I updated it.
Enjoy!
http://www.libertybasicuniversity.com/lbnews/nl108/index.htm
Enjoy!
Sometimes I'm asked how to PEEK and POKE using Liberty BASIC. The short answer to this is, you can't. The long answer is more complicated.
The original home computers (like the classic Commodore 64, TRS-80, and Apple II models) were designed to be completely open to their owners. Most of the different parts of the computer like the sound, graphics, keyboard and joystick (there were no mice back then) were controlled by mapping them to different memory locations. So, the built-in commands didn't do everything you need? It was common to control the computer's equipment directly by sticking values into the memory locations that control that equipment, and reading the status back out. This was done with POKE and PEEK. This was a lot of fun, and usually useful too.
Since these early machines didn't multitask it was pretty safe to steal control of things away from the BASIC interpreter (which was a much operating system as there was). Then computers started running Windows, the Mac OS, Linux, etc. and allowed more than one program to run at a time. PEEK and POKE became problematic because if one program decided to mess with the screen for example, it might interfere with what other programs need to do their thing. Not only that, but computers today all have different kinds of hardware in them, so even if you could PEEK and POKE the exact memory locations would be different from machine to machine. So that's the bad news. :-(
The good news is that we can still have a power trip. How? Your operating system manages all the hardware for you behind operating system functions. These functions make every computer look more or less the same to the programmer. You can use these functions to do a lot of powerful things and most languages have a way to use them. For example Liberty BASIC programmers can use the CALLDLL command to make Windows API calls, which are the operating system calls of the Windows operating system. These are more complicated in general (and you can fill a whole bookshelf with information about them) than the old fashioned PEEK and POKE, but at least we aren't left without some way to pull rank on our computers. ;-)
So if you're missing the power and coolness of PEEK and POKE, why not try your hand at API calls?
Here is a useful link to get you started:
The original home computers (like the classic Commodore 64, TRS-80, and Apple II models) were designed to be completely open to their owners. Most of the different parts of the computer like the sound, graphics, keyboard and joystick (there were no mice back then) were controlled by mapping them to different memory locations. So, the built-in commands didn't do everything you need? It was common to control the computer's equipment directly by sticking values into the memory locations that control that equipment, and reading the status back out. This was done with POKE and PEEK. This was a lot of fun, and usually useful too.
Since these early machines didn't multitask it was pretty safe to steal control of things away from the BASIC interpreter (which was a much operating system as there was). Then computers started running Windows, the Mac OS, Linux, etc. and allowed more than one program to run at a time. PEEK and POKE became problematic because if one program decided to mess with the screen for example, it might interfere with what other programs need to do their thing. Not only that, but computers today all have different kinds of hardware in them, so even if you could PEEK and POKE the exact memory locations would be different from machine to machine. So that's the bad news. :-(
The good news is that we can still have a power trip. How? Your operating system manages all the hardware for you behind operating system functions. These functions make every computer look more or less the same to the programmer. You can use these functions to do a lot of powerful things and most languages have a way to use them. For example Liberty BASIC programmers can use the CALLDLL command to make Windows API calls, which are the operating system calls of the Windows operating system. These are more complicated in general (and you can fill a whole bookshelf with information about them) than the old fashioned PEEK and POKE, but at least we aren't left without some way to pull rank on our computers. ;-)
So if you're missing the power and coolness of PEEK and POKE, why not try your hand at API calls?
Here is a useful link to get you started:
http://www.libertybasicuniversity.com/lbnews/nl108/index.htm
Labels:
api,
api calls,
apple ii,
basic,
calldll,
commodore 64,
gpf,
home computer,
liberty basic,
memory,
memory location,
memory protection,
peek,
poke,
qbasic,
trs-80
High DPI Issues
These days we see the arrival of very high resolution displays such as Apple's retina screens and the 4K monitors. These are very cool, but for programmers they can be a real pain in the side.
Any application designed for a normal monitor will be tiny when viewed on a very high resolution monitor. Windows provides a way to make things larger on screen so that the user isn't left reaching for a magnifying glass, but the effect isn't consistently applied to all the widgets and fonts, so that the user is left with an unsatisfying and unsatisfactory result.
The programming is left with the job of cleaning this up.
Check out the ongoing thread about this in our forum.
http://libertybasic.conforums.com/index.cgi?board=general&action=display&num=1456442864&start=0
Any application designed for a normal monitor will be tiny when viewed on a very high resolution monitor. Windows provides a way to make things larger on screen so that the user isn't left reaching for a magnifying glass, but the effect isn't consistently applied to all the widgets and fonts, so that the user is left with an unsatisfying and unsatisfactory result.
The programming is left with the job of cleaning this up.
Check out the ongoing thread about this in our forum.
http://libertybasic.conforums.com/index.cgi?board=general&action=display&num=1456442864&start=0
Labels:
4k monitor,
basic programming,
display,
dpi,
fonts,
gui,
high dpi,
liberty basic,
mac os,
problems,
programming,
retina,
scaling,
widgets,
windows
Dictionary lookup - bug fixes
One of our eagle-eyed forum members noticed a bug in our dictionary code (thanks to tsh!). In a couple of places the code that dealt with keys was not looking for the end of the key name. This caused problems when two keys started the same. For example let's say we have a key "name" and another key "names of next of kin". The second key starts with "name", and the code wasn't written to avoid a false detection of this sort.
The solution to this problem is to fully qualify the key names by adding the "~value~" tag into the detection code so that when we look for "name" we are actually looking for a match on "~key~name~value~" and not "~key~name", which would give us a false positive if we looking at any key that happened to start with "name"
Here is the fixed version of our dictionary inspector. Enjoy!
nomainwin
WindowWidth = 555
WindowHeight = 438
dim keys$(1000)
dim info$(10, 10)
global dictionary$, keyCount, lastKey$
call readDictionary
texteditor #main.value, 175, 7, 360, 365
listbox #main.keys, keys$(), [keySelected], 5, 7, 160, 365
menu #main, "Key", "New", [newKey], "Delete", [deleteKey]
menu #main, "Edit"
open "Dictionary inspector" for window as #main
#main "trapclose [quit]"
#main.keys "singleclickselect"
#main.value "!autoresize";
call loadKeys
[main.inputLoop] 'wait here for input event
wait
[newKey] 'ask the user for a new key
call saveValue
prompt "Enter a name for the key."; newKey$
if newKey$ <> "" then
call setValueByName newKey$, ""
call loadKeys
#main.keys "select "; newKey$
#main.value "!cls";
#main.value "!setfocus";
call collectGarbage
call writeDictionary
lastKey$ = newKey$
end if
wait
[deleteKey] 'left for later
notice "Delete not implemented yet."
wait
[keySelected] 'a key in the list was selected
call saveValue
#main.keys "selection? selectedKey$"
selectedValue$ = getValue$(selectedKey$)
#main.value "!contents selectedValue$";
lastKey$ = selectedKey$
wait
[quit] 'End the program
call saveValue
close #main
end
sub saveValue 'if the value is changed, save it
if lastKey$ <> "" then
#main.value "!modified? modified$";
if modified$ = "true" then
#main.value "!contents? saveThisValue$";
call setValueByName lastKey$, saveThisValue$
call collectGarbage
call writeDictionary
end if
end if
end sub
function getKeys$(delimiter$)
global keyCount
pointer = 1
while pointer <> 0
'get the next key
pointer = instr(dictionary$, "~key~", pointer)
if pointer then
keyPointer = pointer + 5
pointer = instr(dictionary$, "~value~", pointer)
key$ = mid$(dictionary$, keyPointer, pointer - keyPointer)
if instr(keyList$, "~key~" + key$ + "~value~") = 0 then
getKeys$ = getKeys$ + key$ + delimiter$
keyList$ = keyList$ + "~key~" + key$
keyCount = keyCount + 1
end if
end if
wend
end function
sub writeDictionary
open "dictionary.dat" for output as #writeDict
print #writeDict, dictionary$
close #writeDict
end sub
sub readDictionary
if fileExists(DefaultDir$, "dictionary.dat") then
open "dictionary.dat" for input as #readDict
length = lof(#readDict)
dictionary$ = input$(#readDict, length)
close #readDict
end if
end sub
sub collectGarbage
pointer = 1
while pointer > 0
'get the next key
pointer = instr(dictionary$, "~key~", pointer)
if pointer then
keyPointer = pointer + 5
pointer = instr(dictionary$, "~value~", pointer)
key$ = mid$(dictionary$, keyPointer, pointer - keyPointer)
if instr(keyList$, "~key~" + key$ + "~value~") = 0 then
value$ = getValue$(key$)
newDictionary$ = "~key~" + key$ + "~value~" + value$ + newDictionary$
keyList$ = keyList$ + "~key~" + key$ + "~value~"
end if
end if
wend
dictionary$ = newDictionary$
end sub
sub setValueByName key$, value$
dictionary$ = "~key~"+key$+"~value~"+value$+dictionary$
end sub
function getValue$(key$)
getValue$ = chr$(0)
keyPosition = instr(dictionary$, "~key~"+key$+"~value~")
if keyPosition > 0 then
keyPosition = keyPosition + 5 'skip over key tag
valuePosition = instr(dictionary$, "~value~", keyPosition)
if valuePosition > 0 then
valuePosition = valuePosition + 7 'skip over value tag
endPosition = instr(dictionary$, "~key~", valuePosition)
if endPosition > 0 then
getValue$ = mid$(dictionary$, valuePosition, endPosition - valuePosition)
else
getValue$ = mid$(dictionary$, valuePosition)
end if
end if
end if
end function
sub loadKeys
keyList$ = getKeys$("~")
redim keys$(keyCount)
for item = 1 to keyCount
keys$(item-1) = word$(keyList$, item, "~")
next item
#main.keys "reload"
end sub
function fileExists(path$, filename$) ' Does file exist?
files path$, filename$, info$(
fileExists = val(info$(0, 0)) > 0
end function
The solution to this problem is to fully qualify the key names by adding the "~value~" tag into the detection code so that when we look for "name" we are actually looking for a match on "~key~name~value~" and not "~key~name", which would give us a false positive if we looking at any key that happened to start with "name"
Here is the fixed version of our dictionary inspector. Enjoy!
nomainwin
WindowWidth = 555
WindowHeight = 438
dim keys$(1000)
dim info$(10, 10)
global dictionary$, keyCount, lastKey$
call readDictionary
texteditor #main.value, 175, 7, 360, 365
listbox #main.keys, keys$(), [keySelected], 5, 7, 160, 365
menu #main, "Key", "New", [newKey], "Delete", [deleteKey]
menu #main, "Edit"
open "Dictionary inspector" for window as #main
#main "trapclose [quit]"
#main.keys "singleclickselect"
#main.value "!autoresize";
call loadKeys
[main.inputLoop] 'wait here for input event
wait
[newKey] 'ask the user for a new key
call saveValue
prompt "Enter a name for the key."; newKey$
if newKey$ <> "" then
call setValueByName newKey$, ""
call loadKeys
#main.keys "select "; newKey$
#main.value "!cls";
#main.value "!setfocus";
call collectGarbage
call writeDictionary
lastKey$ = newKey$
end if
wait
[deleteKey] 'left for later
notice "Delete not implemented yet."
wait
[keySelected] 'a key in the list was selected
call saveValue
#main.keys "selection? selectedKey$"
selectedValue$ = getValue$(selectedKey$)
#main.value "!contents selectedValue$";
lastKey$ = selectedKey$
wait
[quit] 'End the program
call saveValue
close #main
end
sub saveValue 'if the value is changed, save it
if lastKey$ <> "" then
#main.value "!modified? modified$";
if modified$ = "true" then
#main.value "!contents? saveThisValue$";
call setValueByName lastKey$, saveThisValue$
call collectGarbage
call writeDictionary
end if
end if
end sub
function getKeys$(delimiter$)
global keyCount
pointer = 1
while pointer <> 0
'get the next key
pointer = instr(dictionary$, "~key~", pointer)
if pointer then
keyPointer = pointer + 5
pointer = instr(dictionary$, "~value~", pointer)
key$ = mid$(dictionary$, keyPointer, pointer - keyPointer)
if instr(keyList$, "~key~" + key$ + "~value~") = 0 then
getKeys$ = getKeys$ + key$ + delimiter$
keyList$ = keyList$ + "~key~" + key$
keyCount = keyCount + 1
end if
end if
wend
end function
sub writeDictionary
open "dictionary.dat" for output as #writeDict
print #writeDict, dictionary$
close #writeDict
end sub
sub readDictionary
if fileExists(DefaultDir$, "dictionary.dat") then
open "dictionary.dat" for input as #readDict
length = lof(#readDict)
dictionary$ = input$(#readDict, length)
close #readDict
end if
end sub
sub collectGarbage
pointer = 1
while pointer > 0
'get the next key
pointer = instr(dictionary$, "~key~", pointer)
if pointer then
keyPointer = pointer + 5
pointer = instr(dictionary$, "~value~", pointer)
key$ = mid$(dictionary$, keyPointer, pointer - keyPointer)
if instr(keyList$, "~key~" + key$ + "~value~") = 0 then
value$ = getValue$(key$)
newDictionary$ = "~key~" + key$ + "~value~" + value$ + newDictionary$
keyList$ = keyList$ + "~key~" + key$ + "~value~"
end if
end if
wend
dictionary$ = newDictionary$
end sub
sub setValueByName key$, value$
dictionary$ = "~key~"+key$+"~value~"+value$+dictionary$
end sub
function getValue$(key$)
getValue$ = chr$(0)
keyPosition = instr(dictionary$, "~key~"+key$+"~value~")
if keyPosition > 0 then
keyPosition = keyPosition + 5 'skip over key tag
valuePosition = instr(dictionary$, "~value~", keyPosition)
if valuePosition > 0 then
valuePosition = valuePosition + 7 'skip over value tag
endPosition = instr(dictionary$, "~key~", valuePosition)
if endPosition > 0 then
getValue$ = mid$(dictionary$, valuePosition, endPosition - valuePosition)
else
getValue$ = mid$(dictionary$, valuePosition)
end if
end if
end if
end function
sub loadKeys
keyList$ = getKeys$("~")
redim keys$(keyCount)
for item = 1 to keyCount
keys$(item-1) = word$(keyList$, item, "~")
next item
#main.keys "reload"
end sub
function fileExists(path$, filename$) ' Does file exist?
files path$, filename$, info$(
fileExists = val(info$(0, 0)) > 0
end function
Subscribe to:
Posts (Atom)