Showing posts with label qbasic. Show all posts
Showing posts with label qbasic. Show all posts

Saturday, November 16, 2019

Have The Courage to Declutter - Best Practices Series


The things in our homes each have their own purpose, and they have their proper place.  In the course of time there is the inevitable accumulation of things, and this can become clutter which is a problem.  This is a way to think about programming.  It is not a perfect metaphor of course but I think it is useful.

Clean Your Room and Take Out the Trash

Living is a messy affair.  We do all kinds of things in our rooms.  Things like eating, opening and reading mail, getting dressed and undressed, play video games, etc.  If we don't stop and put things away and throw out the trash our rooms will unlivable.  How can we apply this when programming? First all if you find that you are commenting out code (in some languages comments are called remarks) then after a while your code will become harder to read and maintain.  Similarly if you notice you never use some part of your code any more it might be a good idea to delete that code.

Be brave and delete the old code.  If you want to keep the code then save the file in a different name so that you won't lose that version of it.  If you are using some kind of source code control (also known as version control) then you will never lose old code.

Don't Be a Hoarder

Hoarders are afraid to get rid of things and they often have many of the same kinds of things until their house fills up with so much stuff that they can't find anything and there is no room to live in their house anymore.

How to implement this idea in programming?  Each idea should only be expressed in one place. Do you notice sometimes when you're writing code that you are doing the same thing again and again so that when you change something then it needs to be changed everywhere you have that similar code?  You need to create a subroutine or a function that does that one thing in only one place, and then call that routine from all those places where the code was duplicated.  This is a kind of code refactoring.

Live In a Tiny House

Imagine your program must fit in a constrained memory.  What is the purpose of this, you might ask?  Today we have what is essentially unlimited memory in our computers.  If you just sat down and wrote code every day for the rest of your life you would never be able to fill up all that memory.  You might not be able to resist the temptation to add every single feature that you can to your software.  People who live in a tiny house often ask themselves what are the essential things, and what can be disposed of?  The more features you have, the more code you have, and the more complicated it is, and this makes it harder to extend and maintain.

    -"Everything should be as simple as possible, but not simpler." -- Albert Einstein

I hope this article is helpful to you and will give you something to incorporate into your work.

Tuesday, November 05, 2019

Naming Things Well - Best Practices Series


Some BASIC programmers come from the experience of needing to fit their programs in 4K of RAM or less, so it feels natural to give variable names single letters such as a, b, c or I, j, k. Of course most classic BASIC interpreters don't have support more for than 2 characters for variables. Some BASICs will let you call your variable 'count' but only the c and o will be recognized as the variable name.

To be fair, I've seen more than a few C programmers also choose single character variable names. Bad habits are language agnostic. ;)

In programming languages today including Liberty BASIC, you can give your variable names and other things such as subroutines and functions longer names. What's more, computers have so much memory that we can dispense with trying to save memory with short variable names. And if you're used to using line numbers I strongly encourage you to discard them in favor of named labels. Once you are used to this it will make your programming better.

Why does it matter?

Programming is an exercise in thinking and so it is really important to pick the best possible names for the parts of your programming ideas. When you or somebody else reads your code it will help to understand the code if the variables and function/sub names are chosen well.

What if you are about to go to the store to buy some milk and you told your friend "I'm g to the s to b some m." How well would you be understood?

So, when you write code ask yourself (or the coder next to you) what is the best name for this variable or routine? Later on you may even realize there is a better name and so don't hesitate to act on that impulse and update your code with the new better name.

Think of your coding as a journey to discover the truth of the code, and naming things is an important part of the discovery.

Wednesday, August 02, 2017

Raspberry Pi Deluxe Starter Kit Giveaway!



We are giving away a deluxe Raspberry Pi kit, and this includes all kinds of nice accessories including an electronics learning lab.  No purchase is required, so enter by Aug 31, 2017 to be included in the raffle.

Check out the video here for details about the kit!  https://youtu.be/toln-Z_z4RY

To enter the drawing, send an email to carlg@justbasic.com with the subject Raspberry Pi Contest, and we will send you a confirmation email. The winner will be announced at the end of August 2017.

Please share this information with your friends.




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.

  • 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!

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!


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:

http://www.libertybasicuniversity.com/lbnews/nl108/index.htm

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


Wednesday, July 20, 2016

A dictionary lookup mini application

Here is a simple demo application for our keyed lookup library in development.  It opens a window with keys on the left, and a text editor on the right.  You can add new keys and edit the values for those keys by changing text on the right.  The values are saved to the file dictionary.dat.

We will enhance this code for a couple more blog entries before we move on to some other project.  :-)

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 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$) = 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$) = 0 then
        value$ = getValue$(key$)
        newDictionary$ = "~key~" + key$ + "~value~" + value$ + newDictionary$
        keyList$ = keyList$ + key$
      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$)
  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


Friday, July 15, 2016

Dictionary lookup - getting the keys

When you have an array you can simply loop through the contents to examine what's there, but when you have a dictionary you need to have the list of keys so that you can look up each value in the dictionary.  For that, we need a getKeys$() function.  The following function returns a single string with the keys from our global dictionary$ variable, each separated by a delimiter that we can specify.

function getKeys$(delimiter$)
  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$) = 0 then
        getKeys$ = getKeys$ + key$ + delimiter$
        keyList$ = keyList$ + "~key~" + key$
      end if
    end if
  wend
end function

Once have this string we can tease out each key.  Here is an quick example that shows how to do this.  The variable allKeys$ will hold all the keys, each separated by "~".  Then we use the word$() function to get each key.

global dictionary$

call setValueByName "first", "Tom"
call setValueByName "last", "Thumb"
call setValueByName "phone", "555-555-1234"

allKeys$ = getKeys$("~")
print allKeys$

key = 1
while word$(allKeys$, key, "~") <> ""
  key$ = word$(allKeys$, key, "~")
  print "Key number "; key; " is "; key$
  print "    value = "; getValue$(key$)
  key = key + 1
wend


Here is what the resulting output looks.

phone~last~first~
Key number 1 is phone
    value = 555-555-1234
Key number 2 is last
    value = Thumb
Key number 3 is first
    value = Tom


Notice that the keys do not come out in the order that we put them in.  This is typical in dictionary style lookup mechanisms.  The ordering of keys is not guaranteed.

Tuesday, July 12, 2016

Liberty BASIC on the Rosetta Code site

One of the members of our community forum posted about some graphics code that he wrote on the Rosetta Code site.

Here is his post.
http://libertybasic.conforums.com/index.cgi?board=LB3&action=display&num=1468265910

I'm glad he posted about it just as a reminder to what a great resource the Rosetta Code site is.  There are many, many code samples there to learn from.  Check out this link.

http://rosettacode.org/wiki/Category:Liberty_BASIC

And, if you'd like to get in on the action there are many Rosetta Code examples which have not yet been implemented in Liberty BASIC.  For a list try this link:

http://rosettacode.org/wiki/Reports:Tasks_not_implemented_in_Liberty_BASIC

These are implemented in other language on the Rosetta Code site so if you'd like to try your hand at writing one or more in Liberty BASIC there are examples to glean inspiration from.

Sunday, October 11, 2009

BASIC and Space Flight!

The same fellow who created an OpenGL flight simulator in Liberty BASIC has taken it a step further by creating a spaceflight simulator with a map editor. Very neat!

Check it out

Monday, September 28, 2009

BASIC, Lasers, and you

Here's an interesting example of the perfect use for BASIC. If you're into laser tag and programming this may be your ticket. It's cool that the makers of this device decided to write the config tool in Liberty BASIC, and that they make the code available so the average Joe can customize it. That's in the spirit of BASIC. Programming for the non-programmer.

http://www.lasertagparts.com/mtmicro.htm

Thursday, February 21, 2008

BASIC is bad because it's too easy?

A response to a recent post read, "BASIC is good, but I think it should be avoided as a first language because it pampers the programmer too much."

I won't be the first person to say that BASIC is perfect. There is no perfect language. However, to recommend that a first language should not "pamper" the beginning programmer seems to me misses the mark completely.

I'm guessing that this means that languages should force the beginner to be aware of low level details such as the type of numeric value (int, float, byte, etc.) or that the beginner should made to manage the allocation and deallocation of memory. What do these sorts of things have to do with the essense of programming? Since there are many languages which do not have these kinds of features, I can only submit that they aren't essential to programming. Therefore they are not necessary ideas to teach the beginner.

The nitty gritty details of how a computer works ARE important. These things should be taught to any serious student of computers, but they do not need to be the first thing taught. People who do not fancy themselves experts do not need to be bothered to learn them.

Easy is the quality that BASIC has, especially in the quickness of its learnability. This is almost to a fault I agree in the sense that a slightly more general and abstract language might be a little harder to warm up to but better in the long haul. However for the person who programs for fun, or who needs a light language for writing utilities or small personal applications, I think BASIC hits the mark pretty well.

Friday, May 04, 2007

Demo of Run BASIC at Smalltalk Solutions

Wednesday evening I had the priviledge of organizing the Seaside BOF (birds of a feather) session at Smalltalk Solutions. This meant that I was the one up at the microphone (which I pushed aside since the room wasn't big) for most of an hour and a half. It was fun and I didn't feel put on the spot since this was more of a round table than anything. I only had to try and direct the conversation some.

Here is James Robertson's blog entry with a photo of me presenting.

http://www.cincomsmalltalk.com/blog/blogView?entry=3355631481

I'm very small in the frame, but yeah that's me all right. You can make out the Run BASIC web page up on the screen if you look carefully.

The demo was a little bit scary since I was modifying the Run BASIC code almost right up to the last second before the meeting. Scott and I were spending most of our free time at the conference working on some simple CSS integration. I'm hoping to put a screencast up to show how it all works.