diff --git a/examples/compilerperformance/perf.p8 b/examples/compilerperformance/perf.p8 new file mode 100644 index 000000000..f6afe096b --- /dev/null +++ b/examples/compilerperformance/perf.p8 @@ -0,0 +1,996 @@ + +; 10 copies of the TextElite example included in one giant program +; meant to test/profile the performance of the compiler on large programs. + + +%import textio +%import conv +%import diskio +%import test_stack +%import perf2 +%import perf3 +%import perf4 +%import perf5 +%import perf6 +%import perf7 +%import perf8 +%import perf9 +%import perf10 +%option no_sysinit +%zeropage basicsafe + + +main { + + const ubyte numforLave = 7 ; Lave is 7th generated planet in galaxy one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy.travel_to(1, numforLave) + market.init(0) ; Lave's market is seeded with 0 + ship.init() + planet.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util.print_10s(ship.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader.do_buy() + 's' -> trader.do_sell() + 'f' -> trader.do_fuel() + 'j' -> trader.do_jump() + 't' -> trader.do_teleport() + 'g' -> trader.do_next_galaxy() + 'i' -> trader.do_info() + 'm' -> trader.do_show_market() + 'l' -> trader.do_local() + 'c' -> trader.do_cash() + 'h' -> trader.do_hold() + '<' -> trader.do_load() + '>' -> trader.do_save() + } + } + } + } +} + +trader { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy + ubyte planet + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship.cash = savedata.cash + ship.Max_cargo = savedata.max_cargo + ship.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship.cargohold, len(ship.cargohold)) + galaxy.travel_to(savedata.galaxy, savedata.planet) + + planet.display(false) + } + + sub do_save() { + savedata.galaxy = galaxy.number + savedata.planet = planet.number + savedata.cash = ship.cash + savedata.max_cargo = ship.Max_cargo + savedata.fuel = ship.fuel + memcopy(ship.cargohold, &savedata.cargo0, len(ship.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship.fuel + ship.fuel = 255 + jump_to_system() + ship.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet = planet.number + ubyte x = planet.x + ubyte y = planet.y + if galaxy.search_closest_planet(input) { + ubyte distance = planet.distance(x, y) + if distance <= ship.fuel { + galaxy.init_market_for_planet() + ship.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy.travel_to(galaxy.number, current_planet) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market.current_price[ci] * amount + txt.print(" Total price: ") + util.print_10s(price) + if price > ship.cash { + txt.print(" Not enough cash!\n") + } else { + ship.cash -= price + ship.cargohold[ci] += amount + market.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market.current_price[ci] * amount + txt.print(" Total price: ") + util.print_10s(price) + ship.cash += price + ship.cargohold[ci] -= amount + market.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship.Max_fuel - ship.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship.Fuel_cost + if price > ship.cash { + txt.print("Not enough cash!\n") + } else { + ship.cash -= price + ship.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy() { + galaxy.travel_to(galaxy.number+1, planet.number) + planet.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet = planet.number + if galaxy.search_closest_planet(input) { + planet.display(false) + } else { + txt.print(" Not found!") + } + galaxy.travel_to(galaxy.number, current_planet) + } else { + planet.display(false) + } + } + + sub do_local() { + galaxy.local_area() + } + + sub do_show_market() { + market.display() + txt.print("\nFuel: ") + util.print_10s(ship.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship.cargo_free()) + txt.print("t\n") + } +} + +ship { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet.print_name_uppercase() + txt.print(" trade market:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util.print_right(13, names[ci]) + txt.print(" ") + util.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxynum) { + number = 1 + planet.number = 255 + seed = [base0, base1, base2] + repeat galaxynum-1 { + nextgalaxy() + } + } + + sub nextgalaxy() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxynum, ubyte system) { + init(galaxynum) + generate_next_planet() ; always at least planet 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet() + } + planet.name = make_current_planet_name() + init_market_for_planet() + } + + sub init_market_for_planet() { + market.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet(uword nameptr) -> ubyte { + ubyte x = planet.x + ubyte y = planet.y + ubyte current_planet_num = planet.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet() + planet.name = make_current_planet_name() + if util.prefix_matches(nameptr, planet.name) { + ubyte distance = planet.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet_num) + + return found + } + + sub local_area() { + ubyte current_planet = planet.number + ubyte px = planet.x + ubyte py = planet.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet() + ubyte distance = planet.distance(px, py) + if distance <= ship.Max_fuel { + if distance <= ship.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet.name = make_current_planet_name() + planet.display(true) + txt.print(" (") + util.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet() { + determine_planet_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet_properties() { + ; create the planet's characteristics + planet.number++ + planet.x = msb(seed[1]) + planet.y = msb(seed[0]) + planet.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet.govtype <= 1 + planet.economy = (planet.economy | 2) + planet.techlevel = (msb(seed[1]) & 3) + (planet.economy ^ 7) + planet.techlevel += planet.govtype >> 1 + if planet.govtype & 1 + planet.techlevel++ + planet.population = 4 * planet.techlevel + planet.economy + planet.population += planet.govtype + 1 + planet.productivity = ((planet.economy ^ 7) + 3) * (planet.govtype + 4) + planet.productivity *= planet.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet.radius = mkword((seed2_msb & 15) + 11, planet.x) + planet.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet.species_is_alien { + planet.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy #") + txt.print_ub(number) + txt.print("\ngalaxy seed0=") + txt.print_uwhex(galaxy.seed[0], true) + txt.print("\ngalaxy seed1=") + txt.print_uwhex(galaxy.seed[1], true) + txt.print("\ngalaxy seed2=") + txt.print_uwhex(galaxy.seed[2], true) + txt.chrout('\n') + } +} + +planet { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet", "world", "place", "little planet", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy, then increases by 1 for each generated planet + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet_result + + reset_rnd() + recursive_soup() + return planet_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf10.p8 b/examples/compilerperformance/perf10.p8 new file mode 100644 index 000000000..411d909d7 --- /dev/null +++ b/examples/compilerperformance/perf10.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy10 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf10 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet10 in galaxy10 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy10.travel_to(1, numforLave) + market10.init(0) ; Lave's market10 is seeded with 0 + ship10.init() + planet10.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util10.print_10s(ship10.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market10 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader10.do_buy() + 's' -> trader10.do_sell() + 'f' -> trader10.do_fuel() + 'j' -> trader10.do_jump() + 't' -> trader10.do_teleport() + 'g' -> trader10.do_next_galaxy10() + 'i' -> trader10.do_info() + 'm' -> trader10.do_show_market10() + 'l' -> trader10.do_local() + 'c' -> trader10.do_cash() + 'h' -> trader10.do_hold() + '<' -> trader10.do_load() + '>' -> trader10.do_save() + } + } + } + } +} + +trader10 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy10 + ubyte planet10 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship10.cash = savedata.cash + ship10.Max_cargo = savedata.max_cargo + ship10.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship10.cargohold, len(ship10.cargohold)) + galaxy10.travel_to(savedata.galaxy10, savedata.planet10) + + planet10.display(false) + } + + sub do_save() { + savedata.galaxy10 = galaxy10.number + savedata.planet10 = planet10.number + savedata.cash = ship10.cash + savedata.max_cargo = ship10.Max_cargo + savedata.fuel = ship10.fuel + memcopy(ship10.cargohold, &savedata.cargo0, len(ship10.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship10.fuel + ship10.fuel = 255 + jump_to_system() + ship10.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet10 = planet10.number + ubyte x = planet10.x + ubyte y = planet10.y + if galaxy10.search_closest_planet10(input) { + ubyte distance = planet10.distance(x, y) + if distance <= ship10.fuel { + galaxy10.init_market10_for_planet10() + ship10.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet10.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy10.travel_to(galaxy10.number, current_planet10) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market10.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market10.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market10.current_price[ci] * amount + txt.print(" Total price: ") + util10.print_10s(price) + if price > ship10.cash { + txt.print(" Not enough cash!\n") + } else { + ship10.cash -= price + ship10.cargohold[ci] += amount + market10.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market10.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship10.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market10.current_price[ci] * amount + txt.print(" Total price: ") + util10.print_10s(price) + ship10.cash += price + ship10.cargohold[ci] -= amount + market10.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship10.Max_fuel - ship10.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship10.Fuel_cost + if price > ship10.cash { + txt.print("Not enough cash!\n") + } else { + ship10.cash -= price + ship10.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship10.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship10.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy10() { + galaxy10.travel_to(galaxy10.number+1, planet10.number) + planet10.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet10 = planet10.number + if galaxy10.search_closest_planet10(input) { + planet10.display(false) + } else { + txt.print(" Not found!") + } + galaxy10.travel_to(galaxy10.number, current_planet10) + } else { + planet10.display(false) + } + } + + sub do_local() { + galaxy10.local_area() + } + + sub do_show_market10() { + market10.display() + txt.print("\nFuel: ") + util10.print_10s(ship10.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship10.cargo_free()) + txt.print("t\n") + } +} + +ship10 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market10.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market10 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet10's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market10 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet10.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet10.print_name_uppercase() + txt.print(" trade market10:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util10.print_right(13, names[ci]) + txt.print(" ") + util10.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship10.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util10.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy10 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy10 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy10num) { + number = 1 + planet10.number = 255 + seed = [base0, base1, base2] + repeat galaxy10num-1 { + nextgalaxy10() + } + } + + sub nextgalaxy10() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy10num, ubyte system) { + init(galaxy10num) + generate_next_planet10() ; always at least planet10 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet10() + } + planet10.name = make_current_planet10_name() + init_market10_for_planet10() + } + + sub init_market10_for_planet10() { + market10.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet10(uword nameptr) -> ubyte { + ubyte x = planet10.x + ubyte y = planet10.y + ubyte current_planet10_num = planet10.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet10() + planet10.name = make_current_planet10_name() + if util10.prefix_matches(nameptr, planet10.name) { + ubyte distance = planet10.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet10_num) + + return found + } + + sub local_area() { + ubyte current_planet10 = planet10.number + ubyte px = planet10.x + ubyte py = planet10.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet10() + ubyte distance = planet10.distance(px, py) + if distance <= ship10.Max_fuel { + if distance <= ship10.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet10.name = make_current_planet10_name() + planet10.display(true) + txt.print(" (") + util10.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet10) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet10() { + determine_planet10_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet10_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet10_properties() { + ; create the planet10's characteristics + planet10.number++ + planet10.x = msb(seed[1]) + planet10.y = msb(seed[0]) + planet10.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet10.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet10.govtype <= 1 + planet10.economy = (planet10.economy | 2) + planet10.techlevel = (msb(seed[1]) & 3) + (planet10.economy ^ 7) + planet10.techlevel += planet10.govtype >> 1 + if planet10.govtype & 1 + planet10.techlevel++ + planet10.population = 4 * planet10.techlevel + planet10.economy + planet10.population += planet10.govtype + 1 + planet10.productivity = ((planet10.economy ^ 7) + 3) * (planet10.govtype + 4) + planet10.productivity *= planet10.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet10.radius = mkword((seed2_msb & 15) + 11, planet10.x) + planet10.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet10.species_is_alien { + planet10.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet10.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet10.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet10.species_kind = (planet10.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet10.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy10 #") + txt.print_ub(number) + txt.print("\ngalaxy10 seed0=") + txt.print_uwhex(galaxy10.seed[0], true) + txt.print("\ngalaxy10 seed1=") + txt.print_uwhex(galaxy10.seed[1], true) + txt.print("\ngalaxy10 seed2=") + txt.print_uwhex(galaxy10.seed[2], true) + txt.chrout('\n') + } +} + +planet10 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship10", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet10 \xB0", "The world \xB0", "This planet10", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet10", "world", "place", "little planet10", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy10, then increases by 1 for each generated planet10 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet10_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet10_result + + reset_rnd() + recursive_soup() + return planet10_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util10 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf2.p8 b/examples/compilerperformance/perf2.p8 new file mode 100644 index 000000000..1fa4e341f --- /dev/null +++ b/examples/compilerperformance/perf2.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy2 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf2 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet2 in galaxy2 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy2.travel_to(1, numforLave) + market2.init(0) ; Lave's market2 is seeded with 0 + ship2.init() + planet2.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util2.print_10s(ship2.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market2 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader2.do_buy() + 's' -> trader2.do_sell() + 'f' -> trader2.do_fuel() + 'j' -> trader2.do_jump() + 't' -> trader2.do_teleport() + 'g' -> trader2.do_next_galaxy2() + 'i' -> trader2.do_info() + 'm' -> trader2.do_show_market2() + 'l' -> trader2.do_local() + 'c' -> trader2.do_cash() + 'h' -> trader2.do_hold() + '<' -> trader2.do_load() + '>' -> trader2.do_save() + } + } + } + } +} + +trader2 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy2 + ubyte planet2 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship2.cash = savedata.cash + ship2.Max_cargo = savedata.max_cargo + ship2.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship2.cargohold, len(ship2.cargohold)) + galaxy2.travel_to(savedata.galaxy2, savedata.planet2) + + planet2.display(false) + } + + sub do_save() { + savedata.galaxy2 = galaxy2.number + savedata.planet2 = planet2.number + savedata.cash = ship2.cash + savedata.max_cargo = ship2.Max_cargo + savedata.fuel = ship2.fuel + memcopy(ship2.cargohold, &savedata.cargo0, len(ship2.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship2.fuel + ship2.fuel = 255 + jump_to_system() + ship2.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet2 = planet2.number + ubyte x = planet2.x + ubyte y = planet2.y + if galaxy2.search_closest_planet2(input) { + ubyte distance = planet2.distance(x, y) + if distance <= ship2.fuel { + galaxy2.init_market2_for_planet2() + ship2.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet2.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy2.travel_to(galaxy2.number, current_planet2) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market2.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market2.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market2.current_price[ci] * amount + txt.print(" Total price: ") + util2.print_10s(price) + if price > ship2.cash { + txt.print(" Not enough cash!\n") + } else { + ship2.cash -= price + ship2.cargohold[ci] += amount + market2.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market2.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship2.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market2.current_price[ci] * amount + txt.print(" Total price: ") + util2.print_10s(price) + ship2.cash += price + ship2.cargohold[ci] -= amount + market2.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship2.Max_fuel - ship2.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship2.Fuel_cost + if price > ship2.cash { + txt.print("Not enough cash!\n") + } else { + ship2.cash -= price + ship2.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship2.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship2.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy2() { + galaxy2.travel_to(galaxy2.number+1, planet2.number) + planet2.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet2 = planet2.number + if galaxy2.search_closest_planet2(input) { + planet2.display(false) + } else { + txt.print(" Not found!") + } + galaxy2.travel_to(galaxy2.number, current_planet2) + } else { + planet2.display(false) + } + } + + sub do_local() { + galaxy2.local_area() + } + + sub do_show_market2() { + market2.display() + txt.print("\nFuel: ") + util2.print_10s(ship2.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship2.cargo_free()) + txt.print("t\n") + } +} + +ship2 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market2.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market2 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet2's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market2 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet2.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet2.print_name_uppercase() + txt.print(" trade market2:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util2.print_right(13, names[ci]) + txt.print(" ") + util2.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship2.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util2.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy2 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy2 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy2num) { + number = 1 + planet2.number = 255 + seed = [base0, base1, base2] + repeat galaxy2num-1 { + nextgalaxy2() + } + } + + sub nextgalaxy2() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy2num, ubyte system) { + init(galaxy2num) + generate_next_planet2() ; always at least planet2 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet2() + } + planet2.name = make_current_planet2_name() + init_market2_for_planet2() + } + + sub init_market2_for_planet2() { + market2.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet2(uword nameptr) -> ubyte { + ubyte x = planet2.x + ubyte y = planet2.y + ubyte current_planet2_num = planet2.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet2() + planet2.name = make_current_planet2_name() + if util2.prefix_matches(nameptr, planet2.name) { + ubyte distance = planet2.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet2_num) + + return found + } + + sub local_area() { + ubyte current_planet2 = planet2.number + ubyte px = planet2.x + ubyte py = planet2.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet2() + ubyte distance = planet2.distance(px, py) + if distance <= ship2.Max_fuel { + if distance <= ship2.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet2.name = make_current_planet2_name() + planet2.display(true) + txt.print(" (") + util2.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet2) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet2() { + determine_planet2_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet2_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet2_properties() { + ; create the planet2's characteristics + planet2.number++ + planet2.x = msb(seed[1]) + planet2.y = msb(seed[0]) + planet2.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet2.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet2.govtype <= 1 + planet2.economy = (planet2.economy | 2) + planet2.techlevel = (msb(seed[1]) & 3) + (planet2.economy ^ 7) + planet2.techlevel += planet2.govtype >> 1 + if planet2.govtype & 1 + planet2.techlevel++ + planet2.population = 4 * planet2.techlevel + planet2.economy + planet2.population += planet2.govtype + 1 + planet2.productivity = ((planet2.economy ^ 7) + 3) * (planet2.govtype + 4) + planet2.productivity *= planet2.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet2.radius = mkword((seed2_msb & 15) + 11, planet2.x) + planet2.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet2.species_is_alien { + planet2.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet2.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet2.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet2.species_kind = (planet2.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet2.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy2 #") + txt.print_ub(number) + txt.print("\ngalaxy2 seed0=") + txt.print_uwhex(galaxy2.seed[0], true) + txt.print("\ngalaxy2 seed1=") + txt.print_uwhex(galaxy2.seed[1], true) + txt.print("\ngalaxy2 seed2=") + txt.print_uwhex(galaxy2.seed[2], true) + txt.chrout('\n') + } +} + +planet2 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship2", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet2 \xB0", "The world \xB0", "This planet2", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet2", "world", "place", "little planet2", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy2, then increases by 1 for each generated planet2 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet2_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet2_result + + reset_rnd() + recursive_soup() + return planet2_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util2 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf3.p8 b/examples/compilerperformance/perf3.p8 new file mode 100644 index 000000000..a022a1869 --- /dev/null +++ b/examples/compilerperformance/perf3.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy3 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf3 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet3 in galaxy3 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy3.travel_to(1, numforLave) + market3.init(0) ; Lave's market3 is seeded with 0 + ship3.init() + planet3.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util3.print_10s(ship3.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market3 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader3.do_buy() + 's' -> trader3.do_sell() + 'f' -> trader3.do_fuel() + 'j' -> trader3.do_jump() + 't' -> trader3.do_teleport() + 'g' -> trader3.do_next_galaxy3() + 'i' -> trader3.do_info() + 'm' -> trader3.do_show_market3() + 'l' -> trader3.do_local() + 'c' -> trader3.do_cash() + 'h' -> trader3.do_hold() + '<' -> trader3.do_load() + '>' -> trader3.do_save() + } + } + } + } +} + +trader3 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy3 + ubyte planet3 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship3.cash = savedata.cash + ship3.Max_cargo = savedata.max_cargo + ship3.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship3.cargohold, len(ship3.cargohold)) + galaxy3.travel_to(savedata.galaxy3, savedata.planet3) + + planet3.display(false) + } + + sub do_save() { + savedata.galaxy3 = galaxy3.number + savedata.planet3 = planet3.number + savedata.cash = ship3.cash + savedata.max_cargo = ship3.Max_cargo + savedata.fuel = ship3.fuel + memcopy(ship3.cargohold, &savedata.cargo0, len(ship3.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship3.fuel + ship3.fuel = 255 + jump_to_system() + ship3.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet3 = planet3.number + ubyte x = planet3.x + ubyte y = planet3.y + if galaxy3.search_closest_planet3(input) { + ubyte distance = planet3.distance(x, y) + if distance <= ship3.fuel { + galaxy3.init_market3_for_planet3() + ship3.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet3.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy3.travel_to(galaxy3.number, current_planet3) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market3.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market3.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market3.current_price[ci] * amount + txt.print(" Total price: ") + util3.print_10s(price) + if price > ship3.cash { + txt.print(" Not enough cash!\n") + } else { + ship3.cash -= price + ship3.cargohold[ci] += amount + market3.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market3.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship3.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market3.current_price[ci] * amount + txt.print(" Total price: ") + util3.print_10s(price) + ship3.cash += price + ship3.cargohold[ci] -= amount + market3.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship3.Max_fuel - ship3.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship3.Fuel_cost + if price > ship3.cash { + txt.print("Not enough cash!\n") + } else { + ship3.cash -= price + ship3.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship3.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship3.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy3() { + galaxy3.travel_to(galaxy3.number+1, planet3.number) + planet3.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet3 = planet3.number + if galaxy3.search_closest_planet3(input) { + planet3.display(false) + } else { + txt.print(" Not found!") + } + galaxy3.travel_to(galaxy3.number, current_planet3) + } else { + planet3.display(false) + } + } + + sub do_local() { + galaxy3.local_area() + } + + sub do_show_market3() { + market3.display() + txt.print("\nFuel: ") + util3.print_10s(ship3.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship3.cargo_free()) + txt.print("t\n") + } +} + +ship3 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market3.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market3 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet3's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market3 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet3.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet3.print_name_uppercase() + txt.print(" trade market3:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util3.print_right(13, names[ci]) + txt.print(" ") + util3.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship3.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util3.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy3 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy3 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy3num) { + number = 1 + planet3.number = 255 + seed = [base0, base1, base2] + repeat galaxy3num-1 { + nextgalaxy3() + } + } + + sub nextgalaxy3() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy3num, ubyte system) { + init(galaxy3num) + generate_next_planet3() ; always at least planet3 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet3() + } + planet3.name = make_current_planet3_name() + init_market3_for_planet3() + } + + sub init_market3_for_planet3() { + market3.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet3(uword nameptr) -> ubyte { + ubyte x = planet3.x + ubyte y = planet3.y + ubyte current_planet3_num = planet3.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet3() + planet3.name = make_current_planet3_name() + if util3.prefix_matches(nameptr, planet3.name) { + ubyte distance = planet3.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet3_num) + + return found + } + + sub local_area() { + ubyte current_planet3 = planet3.number + ubyte px = planet3.x + ubyte py = planet3.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet3() + ubyte distance = planet3.distance(px, py) + if distance <= ship3.Max_fuel { + if distance <= ship3.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet3.name = make_current_planet3_name() + planet3.display(true) + txt.print(" (") + util3.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet3) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet3() { + determine_planet3_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet3_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet3_properties() { + ; create the planet3's characteristics + planet3.number++ + planet3.x = msb(seed[1]) + planet3.y = msb(seed[0]) + planet3.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet3.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet3.govtype <= 1 + planet3.economy = (planet3.economy | 2) + planet3.techlevel = (msb(seed[1]) & 3) + (planet3.economy ^ 7) + planet3.techlevel += planet3.govtype >> 1 + if planet3.govtype & 1 + planet3.techlevel++ + planet3.population = 4 * planet3.techlevel + planet3.economy + planet3.population += planet3.govtype + 1 + planet3.productivity = ((planet3.economy ^ 7) + 3) * (planet3.govtype + 4) + planet3.productivity *= planet3.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet3.radius = mkword((seed2_msb & 15) + 11, planet3.x) + planet3.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet3.species_is_alien { + planet3.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet3.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet3.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet3.species_kind = (planet3.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet3.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy3 #") + txt.print_ub(number) + txt.print("\ngalaxy3 seed0=") + txt.print_uwhex(galaxy3.seed[0], true) + txt.print("\ngalaxy3 seed1=") + txt.print_uwhex(galaxy3.seed[1], true) + txt.print("\ngalaxy3 seed2=") + txt.print_uwhex(galaxy3.seed[2], true) + txt.chrout('\n') + } +} + +planet3 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship3", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet3 \xB0", "The world \xB0", "This planet3", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet3", "world", "place", "little planet3", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy3, then increases by 1 for each generated planet3 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet3_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet3_result + + reset_rnd() + recursive_soup() + return planet3_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util3 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf4.p8 b/examples/compilerperformance/perf4.p8 new file mode 100644 index 000000000..15e43f3d8 --- /dev/null +++ b/examples/compilerperformance/perf4.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy4 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf4 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet4 in galaxy4 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy4.travel_to(1, numforLave) + market4.init(0) ; Lave's market4 is seeded with 0 + ship4.init() + planet4.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util4.print_10s(ship4.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market4 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader4.do_buy() + 's' -> trader4.do_sell() + 'f' -> trader4.do_fuel() + 'j' -> trader4.do_jump() + 't' -> trader4.do_teleport() + 'g' -> trader4.do_next_galaxy4() + 'i' -> trader4.do_info() + 'm' -> trader4.do_show_market4() + 'l' -> trader4.do_local() + 'c' -> trader4.do_cash() + 'h' -> trader4.do_hold() + '<' -> trader4.do_load() + '>' -> trader4.do_save() + } + } + } + } +} + +trader4 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy4 + ubyte planet4 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship4.cash = savedata.cash + ship4.Max_cargo = savedata.max_cargo + ship4.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship4.cargohold, len(ship4.cargohold)) + galaxy4.travel_to(savedata.galaxy4, savedata.planet4) + + planet4.display(false) + } + + sub do_save() { + savedata.galaxy4 = galaxy4.number + savedata.planet4 = planet4.number + savedata.cash = ship4.cash + savedata.max_cargo = ship4.Max_cargo + savedata.fuel = ship4.fuel + memcopy(ship4.cargohold, &savedata.cargo0, len(ship4.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship4.fuel + ship4.fuel = 255 + jump_to_system() + ship4.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet4 = planet4.number + ubyte x = planet4.x + ubyte y = planet4.y + if galaxy4.search_closest_planet4(input) { + ubyte distance = planet4.distance(x, y) + if distance <= ship4.fuel { + galaxy4.init_market4_for_planet4() + ship4.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet4.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy4.travel_to(galaxy4.number, current_planet4) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market4.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market4.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market4.current_price[ci] * amount + txt.print(" Total price: ") + util4.print_10s(price) + if price > ship4.cash { + txt.print(" Not enough cash!\n") + } else { + ship4.cash -= price + ship4.cargohold[ci] += amount + market4.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market4.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship4.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market4.current_price[ci] * amount + txt.print(" Total price: ") + util4.print_10s(price) + ship4.cash += price + ship4.cargohold[ci] -= amount + market4.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship4.Max_fuel - ship4.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship4.Fuel_cost + if price > ship4.cash { + txt.print("Not enough cash!\n") + } else { + ship4.cash -= price + ship4.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship4.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship4.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy4() { + galaxy4.travel_to(galaxy4.number+1, planet4.number) + planet4.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet4 = planet4.number + if galaxy4.search_closest_planet4(input) { + planet4.display(false) + } else { + txt.print(" Not found!") + } + galaxy4.travel_to(galaxy4.number, current_planet4) + } else { + planet4.display(false) + } + } + + sub do_local() { + galaxy4.local_area() + } + + sub do_show_market4() { + market4.display() + txt.print("\nFuel: ") + util4.print_10s(ship4.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship4.cargo_free()) + txt.print("t\n") + } +} + +ship4 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market4.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market4 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet4's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market4 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet4.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet4.print_name_uppercase() + txt.print(" trade market4:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util4.print_right(13, names[ci]) + txt.print(" ") + util4.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship4.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util4.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy4 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy4 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy4num) { + number = 1 + planet4.number = 255 + seed = [base0, base1, base2] + repeat galaxy4num-1 { + nextgalaxy4() + } + } + + sub nextgalaxy4() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy4num, ubyte system) { + init(galaxy4num) + generate_next_planet4() ; always at least planet4 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet4() + } + planet4.name = make_current_planet4_name() + init_market4_for_planet4() + } + + sub init_market4_for_planet4() { + market4.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet4(uword nameptr) -> ubyte { + ubyte x = planet4.x + ubyte y = planet4.y + ubyte current_planet4_num = planet4.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet4() + planet4.name = make_current_planet4_name() + if util4.prefix_matches(nameptr, planet4.name) { + ubyte distance = planet4.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet4_num) + + return found + } + + sub local_area() { + ubyte current_planet4 = planet4.number + ubyte px = planet4.x + ubyte py = planet4.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet4() + ubyte distance = planet4.distance(px, py) + if distance <= ship4.Max_fuel { + if distance <= ship4.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet4.name = make_current_planet4_name() + planet4.display(true) + txt.print(" (") + util4.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet4) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet4() { + determine_planet4_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet4_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet4_properties() { + ; create the planet4's characteristics + planet4.number++ + planet4.x = msb(seed[1]) + planet4.y = msb(seed[0]) + planet4.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet4.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet4.govtype <= 1 + planet4.economy = (planet4.economy | 2) + planet4.techlevel = (msb(seed[1]) & 3) + (planet4.economy ^ 7) + planet4.techlevel += planet4.govtype >> 1 + if planet4.govtype & 1 + planet4.techlevel++ + planet4.population = 4 * planet4.techlevel + planet4.economy + planet4.population += planet4.govtype + 1 + planet4.productivity = ((planet4.economy ^ 7) + 3) * (planet4.govtype + 4) + planet4.productivity *= planet4.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet4.radius = mkword((seed2_msb & 15) + 11, planet4.x) + planet4.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet4.species_is_alien { + planet4.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet4.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet4.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet4.species_kind = (planet4.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet4.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy4 #") + txt.print_ub(number) + txt.print("\ngalaxy4 seed0=") + txt.print_uwhex(galaxy4.seed[0], true) + txt.print("\ngalaxy4 seed1=") + txt.print_uwhex(galaxy4.seed[1], true) + txt.print("\ngalaxy4 seed2=") + txt.print_uwhex(galaxy4.seed[2], true) + txt.chrout('\n') + } +} + +planet4 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship4", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet4 \xB0", "The world \xB0", "This planet4", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet4", "world", "place", "little planet4", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy4, then increases by 1 for each generated planet4 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet4_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet4_result + + reset_rnd() + recursive_soup() + return planet4_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util4 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf5.p8 b/examples/compilerperformance/perf5.p8 new file mode 100644 index 000000000..7ce686b90 --- /dev/null +++ b/examples/compilerperformance/perf5.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy5 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf5 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet5 in galaxy5 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy5.travel_to(1, numforLave) + market5.init(0) ; Lave's market5 is seeded with 0 + ship5.init() + planet5.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util5.print_10s(ship5.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market5 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader5.do_buy() + 's' -> trader5.do_sell() + 'f' -> trader5.do_fuel() + 'j' -> trader5.do_jump() + 't' -> trader5.do_teleport() + 'g' -> trader5.do_next_galaxy5() + 'i' -> trader5.do_info() + 'm' -> trader5.do_show_market5() + 'l' -> trader5.do_local() + 'c' -> trader5.do_cash() + 'h' -> trader5.do_hold() + '<' -> trader5.do_load() + '>' -> trader5.do_save() + } + } + } + } +} + +trader5 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy5 + ubyte planet5 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship5.cash = savedata.cash + ship5.Max_cargo = savedata.max_cargo + ship5.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship5.cargohold, len(ship5.cargohold)) + galaxy5.travel_to(savedata.galaxy5, savedata.planet5) + + planet5.display(false) + } + + sub do_save() { + savedata.galaxy5 = galaxy5.number + savedata.planet5 = planet5.number + savedata.cash = ship5.cash + savedata.max_cargo = ship5.Max_cargo + savedata.fuel = ship5.fuel + memcopy(ship5.cargohold, &savedata.cargo0, len(ship5.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship5.fuel + ship5.fuel = 255 + jump_to_system() + ship5.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet5 = planet5.number + ubyte x = planet5.x + ubyte y = planet5.y + if galaxy5.search_closest_planet5(input) { + ubyte distance = planet5.distance(x, y) + if distance <= ship5.fuel { + galaxy5.init_market5_for_planet5() + ship5.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet5.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy5.travel_to(galaxy5.number, current_planet5) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market5.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market5.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market5.current_price[ci] * amount + txt.print(" Total price: ") + util5.print_10s(price) + if price > ship5.cash { + txt.print(" Not enough cash!\n") + } else { + ship5.cash -= price + ship5.cargohold[ci] += amount + market5.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market5.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship5.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market5.current_price[ci] * amount + txt.print(" Total price: ") + util5.print_10s(price) + ship5.cash += price + ship5.cargohold[ci] -= amount + market5.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship5.Max_fuel - ship5.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship5.Fuel_cost + if price > ship5.cash { + txt.print("Not enough cash!\n") + } else { + ship5.cash -= price + ship5.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship5.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship5.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy5() { + galaxy5.travel_to(galaxy5.number+1, planet5.number) + planet5.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet5 = planet5.number + if galaxy5.search_closest_planet5(input) { + planet5.display(false) + } else { + txt.print(" Not found!") + } + galaxy5.travel_to(galaxy5.number, current_planet5) + } else { + planet5.display(false) + } + } + + sub do_local() { + galaxy5.local_area() + } + + sub do_show_market5() { + market5.display() + txt.print("\nFuel: ") + util5.print_10s(ship5.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship5.cargo_free()) + txt.print("t\n") + } +} + +ship5 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market5.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market5 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet5's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market5 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet5.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet5.print_name_uppercase() + txt.print(" trade market5:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util5.print_right(13, names[ci]) + txt.print(" ") + util5.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship5.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util5.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy5 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy5 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy5num) { + number = 1 + planet5.number = 255 + seed = [base0, base1, base2] + repeat galaxy5num-1 { + nextgalaxy5() + } + } + + sub nextgalaxy5() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy5num, ubyte system) { + init(galaxy5num) + generate_next_planet5() ; always at least planet5 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet5() + } + planet5.name = make_current_planet5_name() + init_market5_for_planet5() + } + + sub init_market5_for_planet5() { + market5.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet5(uword nameptr) -> ubyte { + ubyte x = planet5.x + ubyte y = planet5.y + ubyte current_planet5_num = planet5.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet5() + planet5.name = make_current_planet5_name() + if util5.prefix_matches(nameptr, planet5.name) { + ubyte distance = planet5.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet5_num) + + return found + } + + sub local_area() { + ubyte current_planet5 = planet5.number + ubyte px = planet5.x + ubyte py = planet5.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet5() + ubyte distance = planet5.distance(px, py) + if distance <= ship5.Max_fuel { + if distance <= ship5.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet5.name = make_current_planet5_name() + planet5.display(true) + txt.print(" (") + util5.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet5) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet5() { + determine_planet5_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet5_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet5_properties() { + ; create the planet5's characteristics + planet5.number++ + planet5.x = msb(seed[1]) + planet5.y = msb(seed[0]) + planet5.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet5.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet5.govtype <= 1 + planet5.economy = (planet5.economy | 2) + planet5.techlevel = (msb(seed[1]) & 3) + (planet5.economy ^ 7) + planet5.techlevel += planet5.govtype >> 1 + if planet5.govtype & 1 + planet5.techlevel++ + planet5.population = 4 * planet5.techlevel + planet5.economy + planet5.population += planet5.govtype + 1 + planet5.productivity = ((planet5.economy ^ 7) + 3) * (planet5.govtype + 4) + planet5.productivity *= planet5.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet5.radius = mkword((seed2_msb & 15) + 11, planet5.x) + planet5.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet5.species_is_alien { + planet5.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet5.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet5.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet5.species_kind = (planet5.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet5.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy5 #") + txt.print_ub(number) + txt.print("\ngalaxy5 seed0=") + txt.print_uwhex(galaxy5.seed[0], true) + txt.print("\ngalaxy5 seed1=") + txt.print_uwhex(galaxy5.seed[1], true) + txt.print("\ngalaxy5 seed2=") + txt.print_uwhex(galaxy5.seed[2], true) + txt.chrout('\n') + } +} + +planet5 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship5", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet5 \xB0", "The world \xB0", "This planet5", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet5", "world", "place", "little planet5", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy5, then increases by 1 for each generated planet5 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet5_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet5_result + + reset_rnd() + recursive_soup() + return planet5_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util5 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf6.p8 b/examples/compilerperformance/perf6.p8 new file mode 100644 index 000000000..b64943712 --- /dev/null +++ b/examples/compilerperformance/perf6.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy6 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf6 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet6 in galaxy6 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy6.travel_to(1, numforLave) + market6.init(0) ; Lave's market6 is seeded with 0 + ship6.init() + planet6.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util6.print_10s(ship6.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market6 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader6.do_buy() + 's' -> trader6.do_sell() + 'f' -> trader6.do_fuel() + 'j' -> trader6.do_jump() + 't' -> trader6.do_teleport() + 'g' -> trader6.do_next_galaxy6() + 'i' -> trader6.do_info() + 'm' -> trader6.do_show_market6() + 'l' -> trader6.do_local() + 'c' -> trader6.do_cash() + 'h' -> trader6.do_hold() + '<' -> trader6.do_load() + '>' -> trader6.do_save() + } + } + } + } +} + +trader6 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy6 + ubyte planet6 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship6.cash = savedata.cash + ship6.Max_cargo = savedata.max_cargo + ship6.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship6.cargohold, len(ship6.cargohold)) + galaxy6.travel_to(savedata.galaxy6, savedata.planet6) + + planet6.display(false) + } + + sub do_save() { + savedata.galaxy6 = galaxy6.number + savedata.planet6 = planet6.number + savedata.cash = ship6.cash + savedata.max_cargo = ship6.Max_cargo + savedata.fuel = ship6.fuel + memcopy(ship6.cargohold, &savedata.cargo0, len(ship6.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship6.fuel + ship6.fuel = 255 + jump_to_system() + ship6.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet6 = planet6.number + ubyte x = planet6.x + ubyte y = planet6.y + if galaxy6.search_closest_planet6(input) { + ubyte distance = planet6.distance(x, y) + if distance <= ship6.fuel { + galaxy6.init_market6_for_planet6() + ship6.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet6.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy6.travel_to(galaxy6.number, current_planet6) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market6.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market6.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market6.current_price[ci] * amount + txt.print(" Total price: ") + util6.print_10s(price) + if price > ship6.cash { + txt.print(" Not enough cash!\n") + } else { + ship6.cash -= price + ship6.cargohold[ci] += amount + market6.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market6.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship6.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market6.current_price[ci] * amount + txt.print(" Total price: ") + util6.print_10s(price) + ship6.cash += price + ship6.cargohold[ci] -= amount + market6.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship6.Max_fuel - ship6.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship6.Fuel_cost + if price > ship6.cash { + txt.print("Not enough cash!\n") + } else { + ship6.cash -= price + ship6.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship6.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship6.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy6() { + galaxy6.travel_to(galaxy6.number+1, planet6.number) + planet6.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet6 = planet6.number + if galaxy6.search_closest_planet6(input) { + planet6.display(false) + } else { + txt.print(" Not found!") + } + galaxy6.travel_to(galaxy6.number, current_planet6) + } else { + planet6.display(false) + } + } + + sub do_local() { + galaxy6.local_area() + } + + sub do_show_market6() { + market6.display() + txt.print("\nFuel: ") + util6.print_10s(ship6.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship6.cargo_free()) + txt.print("t\n") + } +} + +ship6 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market6.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market6 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet6's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market6 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet6.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet6.print_name_uppercase() + txt.print(" trade market6:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util6.print_right(13, names[ci]) + txt.print(" ") + util6.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship6.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util6.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy6 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy6 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy6num) { + number = 1 + planet6.number = 255 + seed = [base0, base1, base2] + repeat galaxy6num-1 { + nextgalaxy6() + } + } + + sub nextgalaxy6() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy6num, ubyte system) { + init(galaxy6num) + generate_next_planet6() ; always at least planet6 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet6() + } + planet6.name = make_current_planet6_name() + init_market6_for_planet6() + } + + sub init_market6_for_planet6() { + market6.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet6(uword nameptr) -> ubyte { + ubyte x = planet6.x + ubyte y = planet6.y + ubyte current_planet6_num = planet6.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet6() + planet6.name = make_current_planet6_name() + if util6.prefix_matches(nameptr, planet6.name) { + ubyte distance = planet6.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet6_num) + + return found + } + + sub local_area() { + ubyte current_planet6 = planet6.number + ubyte px = planet6.x + ubyte py = planet6.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet6() + ubyte distance = planet6.distance(px, py) + if distance <= ship6.Max_fuel { + if distance <= ship6.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet6.name = make_current_planet6_name() + planet6.display(true) + txt.print(" (") + util6.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet6) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet6() { + determine_planet6_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet6_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet6_properties() { + ; create the planet6's characteristics + planet6.number++ + planet6.x = msb(seed[1]) + planet6.y = msb(seed[0]) + planet6.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet6.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet6.govtype <= 1 + planet6.economy = (planet6.economy | 2) + planet6.techlevel = (msb(seed[1]) & 3) + (planet6.economy ^ 7) + planet6.techlevel += planet6.govtype >> 1 + if planet6.govtype & 1 + planet6.techlevel++ + planet6.population = 4 * planet6.techlevel + planet6.economy + planet6.population += planet6.govtype + 1 + planet6.productivity = ((planet6.economy ^ 7) + 3) * (planet6.govtype + 4) + planet6.productivity *= planet6.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet6.radius = mkword((seed2_msb & 15) + 11, planet6.x) + planet6.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet6.species_is_alien { + planet6.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet6.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet6.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet6.species_kind = (planet6.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet6.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy6 #") + txt.print_ub(number) + txt.print("\ngalaxy6 seed0=") + txt.print_uwhex(galaxy6.seed[0], true) + txt.print("\ngalaxy6 seed1=") + txt.print_uwhex(galaxy6.seed[1], true) + txt.print("\ngalaxy6 seed2=") + txt.print_uwhex(galaxy6.seed[2], true) + txt.chrout('\n') + } +} + +planet6 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship6", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet6 \xB0", "The world \xB0", "This planet6", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet6", "world", "place", "little planet6", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy6, then increases by 1 for each generated planet6 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet6_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet6_result + + reset_rnd() + recursive_soup() + return planet6_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util6 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf7.p8 b/examples/compilerperformance/perf7.p8 new file mode 100644 index 000000000..92389ed1a --- /dev/null +++ b/examples/compilerperformance/perf7.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy7 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf7 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet7 in galaxy7 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy7.travel_to(1, numforLave) + market7.init(0) ; Lave's market7 is seeded with 0 + ship7.init() + planet7.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util7.print_10s(ship7.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market7 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader7.do_buy() + 's' -> trader7.do_sell() + 'f' -> trader7.do_fuel() + 'j' -> trader7.do_jump() + 't' -> trader7.do_teleport() + 'g' -> trader7.do_next_galaxy7() + 'i' -> trader7.do_info() + 'm' -> trader7.do_show_market7() + 'l' -> trader7.do_local() + 'c' -> trader7.do_cash() + 'h' -> trader7.do_hold() + '<' -> trader7.do_load() + '>' -> trader7.do_save() + } + } + } + } +} + +trader7 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy7 + ubyte planet7 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship7.cash = savedata.cash + ship7.Max_cargo = savedata.max_cargo + ship7.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship7.cargohold, len(ship7.cargohold)) + galaxy7.travel_to(savedata.galaxy7, savedata.planet7) + + planet7.display(false) + } + + sub do_save() { + savedata.galaxy7 = galaxy7.number + savedata.planet7 = planet7.number + savedata.cash = ship7.cash + savedata.max_cargo = ship7.Max_cargo + savedata.fuel = ship7.fuel + memcopy(ship7.cargohold, &savedata.cargo0, len(ship7.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship7.fuel + ship7.fuel = 255 + jump_to_system() + ship7.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet7 = planet7.number + ubyte x = planet7.x + ubyte y = planet7.y + if galaxy7.search_closest_planet7(input) { + ubyte distance = planet7.distance(x, y) + if distance <= ship7.fuel { + galaxy7.init_market7_for_planet7() + ship7.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet7.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy7.travel_to(galaxy7.number, current_planet7) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market7.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market7.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market7.current_price[ci] * amount + txt.print(" Total price: ") + util7.print_10s(price) + if price > ship7.cash { + txt.print(" Not enough cash!\n") + } else { + ship7.cash -= price + ship7.cargohold[ci] += amount + market7.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market7.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship7.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market7.current_price[ci] * amount + txt.print(" Total price: ") + util7.print_10s(price) + ship7.cash += price + ship7.cargohold[ci] -= amount + market7.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship7.Max_fuel - ship7.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship7.Fuel_cost + if price > ship7.cash { + txt.print("Not enough cash!\n") + } else { + ship7.cash -= price + ship7.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship7.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship7.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy7() { + galaxy7.travel_to(galaxy7.number+1, planet7.number) + planet7.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet7 = planet7.number + if galaxy7.search_closest_planet7(input) { + planet7.display(false) + } else { + txt.print(" Not found!") + } + galaxy7.travel_to(galaxy7.number, current_planet7) + } else { + planet7.display(false) + } + } + + sub do_local() { + galaxy7.local_area() + } + + sub do_show_market7() { + market7.display() + txt.print("\nFuel: ") + util7.print_10s(ship7.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship7.cargo_free()) + txt.print("t\n") + } +} + +ship7 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market7.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market7 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet7's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market7 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet7.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet7.print_name_uppercase() + txt.print(" trade market7:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util7.print_right(13, names[ci]) + txt.print(" ") + util7.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship7.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util7.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy7 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy7 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy7num) { + number = 1 + planet7.number = 255 + seed = [base0, base1, base2] + repeat galaxy7num-1 { + nextgalaxy7() + } + } + + sub nextgalaxy7() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy7num, ubyte system) { + init(galaxy7num) + generate_next_planet7() ; always at least planet7 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet7() + } + planet7.name = make_current_planet7_name() + init_market7_for_planet7() + } + + sub init_market7_for_planet7() { + market7.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet7(uword nameptr) -> ubyte { + ubyte x = planet7.x + ubyte y = planet7.y + ubyte current_planet7_num = planet7.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet7() + planet7.name = make_current_planet7_name() + if util7.prefix_matches(nameptr, planet7.name) { + ubyte distance = planet7.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet7_num) + + return found + } + + sub local_area() { + ubyte current_planet7 = planet7.number + ubyte px = planet7.x + ubyte py = planet7.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet7() + ubyte distance = planet7.distance(px, py) + if distance <= ship7.Max_fuel { + if distance <= ship7.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet7.name = make_current_planet7_name() + planet7.display(true) + txt.print(" (") + util7.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet7) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet7() { + determine_planet7_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet7_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet7_properties() { + ; create the planet7's characteristics + planet7.number++ + planet7.x = msb(seed[1]) + planet7.y = msb(seed[0]) + planet7.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet7.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet7.govtype <= 1 + planet7.economy = (planet7.economy | 2) + planet7.techlevel = (msb(seed[1]) & 3) + (planet7.economy ^ 7) + planet7.techlevel += planet7.govtype >> 1 + if planet7.govtype & 1 + planet7.techlevel++ + planet7.population = 4 * planet7.techlevel + planet7.economy + planet7.population += planet7.govtype + 1 + planet7.productivity = ((planet7.economy ^ 7) + 3) * (planet7.govtype + 4) + planet7.productivity *= planet7.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet7.radius = mkword((seed2_msb & 15) + 11, planet7.x) + planet7.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet7.species_is_alien { + planet7.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet7.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet7.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet7.species_kind = (planet7.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet7.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy7 #") + txt.print_ub(number) + txt.print("\ngalaxy7 seed0=") + txt.print_uwhex(galaxy7.seed[0], true) + txt.print("\ngalaxy7 seed1=") + txt.print_uwhex(galaxy7.seed[1], true) + txt.print("\ngalaxy7 seed2=") + txt.print_uwhex(galaxy7.seed[2], true) + txt.chrout('\n') + } +} + +planet7 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship7", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet7 \xB0", "The world \xB0", "This planet7", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet7", "world", "place", "little planet7", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy7, then increases by 1 for each generated planet7 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet7_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet7_result + + reset_rnd() + recursive_soup() + return planet7_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util7 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf8.p8 b/examples/compilerperformance/perf8.p8 new file mode 100644 index 000000000..19fcebea3 --- /dev/null +++ b/examples/compilerperformance/perf8.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy8 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf8 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet8 in galaxy8 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy8.travel_to(1, numforLave) + market8.init(0) ; Lave's market8 is seeded with 0 + ship8.init() + planet8.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util8.print_10s(ship8.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market8 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader8.do_buy() + 's' -> trader8.do_sell() + 'f' -> trader8.do_fuel() + 'j' -> trader8.do_jump() + 't' -> trader8.do_teleport() + 'g' -> trader8.do_next_galaxy8() + 'i' -> trader8.do_info() + 'm' -> trader8.do_show_market8() + 'l' -> trader8.do_local() + 'c' -> trader8.do_cash() + 'h' -> trader8.do_hold() + '<' -> trader8.do_load() + '>' -> trader8.do_save() + } + } + } + } +} + +trader8 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy8 + ubyte planet8 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship8.cash = savedata.cash + ship8.Max_cargo = savedata.max_cargo + ship8.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship8.cargohold, len(ship8.cargohold)) + galaxy8.travel_to(savedata.galaxy8, savedata.planet8) + + planet8.display(false) + } + + sub do_save() { + savedata.galaxy8 = galaxy8.number + savedata.planet8 = planet8.number + savedata.cash = ship8.cash + savedata.max_cargo = ship8.Max_cargo + savedata.fuel = ship8.fuel + memcopy(ship8.cargohold, &savedata.cargo0, len(ship8.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship8.fuel + ship8.fuel = 255 + jump_to_system() + ship8.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet8 = planet8.number + ubyte x = planet8.x + ubyte y = planet8.y + if galaxy8.search_closest_planet8(input) { + ubyte distance = planet8.distance(x, y) + if distance <= ship8.fuel { + galaxy8.init_market8_for_planet8() + ship8.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet8.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy8.travel_to(galaxy8.number, current_planet8) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market8.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market8.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market8.current_price[ci] * amount + txt.print(" Total price: ") + util8.print_10s(price) + if price > ship8.cash { + txt.print(" Not enough cash!\n") + } else { + ship8.cash -= price + ship8.cargohold[ci] += amount + market8.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market8.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship8.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market8.current_price[ci] * amount + txt.print(" Total price: ") + util8.print_10s(price) + ship8.cash += price + ship8.cargohold[ci] -= amount + market8.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship8.Max_fuel - ship8.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship8.Fuel_cost + if price > ship8.cash { + txt.print("Not enough cash!\n") + } else { + ship8.cash -= price + ship8.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship8.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship8.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy8() { + galaxy8.travel_to(galaxy8.number+1, planet8.number) + planet8.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet8 = planet8.number + if galaxy8.search_closest_planet8(input) { + planet8.display(false) + } else { + txt.print(" Not found!") + } + galaxy8.travel_to(galaxy8.number, current_planet8) + } else { + planet8.display(false) + } + } + + sub do_local() { + galaxy8.local_area() + } + + sub do_show_market8() { + market8.display() + txt.print("\nFuel: ") + util8.print_10s(ship8.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship8.cargo_free()) + txt.print("t\n") + } +} + +ship8 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market8.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market8 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet8's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market8 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet8.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet8.print_name_uppercase() + txt.print(" trade market8:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util8.print_right(13, names[ci]) + txt.print(" ") + util8.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship8.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util8.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy8 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy8 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy8num) { + number = 1 + planet8.number = 255 + seed = [base0, base1, base2] + repeat galaxy8num-1 { + nextgalaxy8() + } + } + + sub nextgalaxy8() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy8num, ubyte system) { + init(galaxy8num) + generate_next_planet8() ; always at least planet8 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet8() + } + planet8.name = make_current_planet8_name() + init_market8_for_planet8() + } + + sub init_market8_for_planet8() { + market8.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet8(uword nameptr) -> ubyte { + ubyte x = planet8.x + ubyte y = planet8.y + ubyte current_planet8_num = planet8.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet8() + planet8.name = make_current_planet8_name() + if util8.prefix_matches(nameptr, planet8.name) { + ubyte distance = planet8.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet8_num) + + return found + } + + sub local_area() { + ubyte current_planet8 = planet8.number + ubyte px = planet8.x + ubyte py = planet8.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet8() + ubyte distance = planet8.distance(px, py) + if distance <= ship8.Max_fuel { + if distance <= ship8.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet8.name = make_current_planet8_name() + planet8.display(true) + txt.print(" (") + util8.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet8) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet8() { + determine_planet8_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet8_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet8_properties() { + ; create the planet8's characteristics + planet8.number++ + planet8.x = msb(seed[1]) + planet8.y = msb(seed[0]) + planet8.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet8.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet8.govtype <= 1 + planet8.economy = (planet8.economy | 2) + planet8.techlevel = (msb(seed[1]) & 3) + (planet8.economy ^ 7) + planet8.techlevel += planet8.govtype >> 1 + if planet8.govtype & 1 + planet8.techlevel++ + planet8.population = 4 * planet8.techlevel + planet8.economy + planet8.population += planet8.govtype + 1 + planet8.productivity = ((planet8.economy ^ 7) + 3) * (planet8.govtype + 4) + planet8.productivity *= planet8.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet8.radius = mkword((seed2_msb & 15) + 11, planet8.x) + planet8.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet8.species_is_alien { + planet8.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet8.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet8.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet8.species_kind = (planet8.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet8.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy8 #") + txt.print_ub(number) + txt.print("\ngalaxy8 seed0=") + txt.print_uwhex(galaxy8.seed[0], true) + txt.print("\ngalaxy8 seed1=") + txt.print_uwhex(galaxy8.seed[1], true) + txt.print("\ngalaxy8 seed2=") + txt.print_uwhex(galaxy8.seed[2], true) + txt.chrout('\n') + } +} + +planet8 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship8", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet8 \xB0", "The world \xB0", "This planet8", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet8", "world", "place", "little planet8", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy8, then increases by 1 for each generated planet8 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet8_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet8_result + + reset_rnd() + recursive_soup() + return planet8_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util8 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +} diff --git a/examples/compilerperformance/perf9.p8 b/examples/compilerperformance/perf9.p8 new file mode 100644 index 000000000..733684595 --- /dev/null +++ b/examples/compilerperformance/perf9.p8 @@ -0,0 +1,986 @@ +%import textio +%import conv +%import diskio +%import test_stack +%option no_sysinit +%zeropage basicsafe + +; Prog8 adaptation of the Text-Elite galaxy9 system trading simulation engine. +; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm + +; Note: this program is compatible with C64 and CX16. + +perf9 { + + const ubyte numforLave = 7 ; Lave is 7th generated planet9 in galaxy9 one + const ubyte numforZaonce = 129 + const ubyte numforDiso = 147 + const ubyte numforRiedquat = 46 + + sub start() { + txt.lowercase() + txt.print("\u000c\n --- TextElite v1.1 ---\n") + + galaxy9.travel_to(1, numforLave) + market9.init(0) ; Lave's market9 is seeded with 0 + ship9.init() + planet9.display(false) + + repeat { + ; test_stack.test() + + str input = "????????" + txt.print("\nCash: ") + util9.print_10s(ship9.cash) + txt.print("\nCommand (?=help): ") + ubyte num_chars = txt.input_chars(input) + txt.chrout('\n') + if num_chars { + when input[0] { + '?' -> { + txt.print("\nCommands are:\n"+ + "buy jump info cash >=save\n"+ + "sell teleport market9 hold <=load\n"+ + "fuel galhyp local quit\n") + } + 'q' -> break + 'b' -> trader9.do_buy() + 's' -> trader9.do_sell() + 'f' -> trader9.do_fuel() + 'j' -> trader9.do_jump() + 't' -> trader9.do_teleport() + 'g' -> trader9.do_next_galaxy9() + 'i' -> trader9.do_info() + 'm' -> trader9.do_show_market9() + 'l' -> trader9.do_local() + 'c' -> trader9.do_cash() + 'h' -> trader9.do_hold() + '<' -> trader9.do_load() + '>' -> trader9.do_save() + } + } + } + } +} + +trader9 { + str Savegame = "↑commander.save" + str input = "??????????" + ubyte num_chars + + struct SaveData { + ubyte galaxy9 + ubyte planet9 + ubyte cargo0 + ubyte cargo1 + ubyte cargo2 + ubyte cargo3 + ubyte cargo4 + ubyte cargo5 + ubyte cargo6 + ubyte cargo7 + ubyte cargo8 + ubyte cargo9 + ubyte cargo10 + ubyte cargo11 + ubyte cargo12 + ubyte cargo13 + ubyte cargo14 + ubyte cargo15 + ubyte cargo16 + uword cash + ubyte max_cargo + ubyte fuel + } + SaveData savedata + + sub do_load() { + txt.print("\nLoading universe...") + if diskio.load(8, Savegame, &savedata) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + return + } + + ship9.cash = savedata.cash + ship9.Max_cargo = savedata.max_cargo + ship9.fuel = savedata.fuel + memcopy(&savedata.cargo0, ship9.cargohold, len(ship9.cargohold)) + galaxy9.travel_to(savedata.galaxy9, savedata.planet9) + + planet9.display(false) + } + + sub do_save() { + savedata.galaxy9 = galaxy9.number + savedata.planet9 = planet9.number + savedata.cash = ship9.cash + savedata.max_cargo = ship9.Max_cargo + savedata.fuel = ship9.fuel + memcopy(ship9.cargohold, &savedata.cargo0, len(ship9.cargohold)) + + txt.print("\nSaving universe...") + diskio.delete(8, Savegame) + if diskio.save(8, Savegame, &savedata, sizeof(savedata)) { + txt.print("ok\n") + } else { + txt.print("\ni/o error: ") + txt.print(diskio.status(8)) + txt.chrout('\n') + } + } + + sub do_jump() { + txt.print("\nJump to what system? ") + jump_to_system() + } + + sub do_teleport() { + txt.print("\nCheat! Teleport to what system? ") + ubyte fuel = ship9.fuel + ship9.fuel = 255 + jump_to_system() + ship9.fuel = fuel + } + + sub jump_to_system() { + void txt.input_chars(input) + ubyte current_planet9 = planet9.number + ubyte x = planet9.x + ubyte y = planet9.y + if galaxy9.search_closest_planet9(input) { + ubyte distance = planet9.distance(x, y) + if distance <= ship9.fuel { + galaxy9.init_market9_for_planet9() + ship9.fuel -= distance + txt.print("\n\nHyperspace jump! Arrived at:\n") + planet9.display(true) + return + } + txt.print("Insufficient fuel\n") + } else { + txt.print(" Not found!\n") + } + galaxy9.travel_to(galaxy9.number, current_planet9) + } + + sub do_buy() { + txt.print("\nBuy what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market9.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if market9.current_quantity[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market9.current_price[ci] * amount + txt.print(" Total price: ") + util9.print_10s(price) + if price > ship9.cash { + txt.print(" Not enough cash!\n") + } else { + ship9.cash -= price + ship9.cargohold[ci] += amount + market9.current_quantity[ci] -= amount + } + } + } + } + + sub do_sell() { + txt.print("\nSell what commodity? ") + str commodity = "???????????????" + void txt.input_chars(commodity) + ubyte ci = market9.match(commodity) + if ci & 128 { + txt.print("Unknown\n") + } else { + txt.print("\nHow much? ") + void txt.input_chars(input) + ubyte amount = conv.str2ubyte(input) + if ship9.cargohold[ci] < amount { + txt.print(" Insufficient supply!\n") + } else { + uword price = market9.current_price[ci] * amount + txt.print(" Total price: ") + util9.print_10s(price) + ship9.cash += price + ship9.cargohold[ci] -= amount + market9.current_quantity[ci] += amount + } + } + } + + sub do_fuel() { + txt.print("\nBuy fuel. Amount? ") + void txt.input_chars(input) + ubyte buy_fuel = 10*conv.str2ubyte(input) + ubyte max_fuel = ship9.Max_fuel - ship9.fuel + if buy_fuel > max_fuel + buy_fuel = max_fuel + uword price = buy_fuel as uword * ship9.Fuel_cost + if price > ship9.cash { + txt.print("Not enough cash!\n") + } else { + ship9.cash -= price + ship9.fuel += buy_fuel + } + } + + sub do_cash() { + txt.print("\nCheat! Set cash amount: ") + void txt.input_chars(input) + ship9.cash = conv.str2uword(input) + } + + sub do_hold() { + txt.print("\nCheat! Set cargohold size: ") + void txt.input_chars(input) + ship9.Max_cargo = conv.str2ubyte(input) + } + + sub do_next_galaxy9() { + galaxy9.travel_to(galaxy9.number+1, planet9.number) + planet9.display(false) + } + + sub do_info() { + txt.print("\nSystem name (empty=current): ") + num_chars = txt.input_chars(input) + if num_chars { + ubyte current_planet9 = planet9.number + if galaxy9.search_closest_planet9(input) { + planet9.display(false) + } else { + txt.print(" Not found!") + } + galaxy9.travel_to(galaxy9.number, current_planet9) + } else { + planet9.display(false) + } + } + + sub do_local() { + galaxy9.local_area() + } + + sub do_show_market9() { + market9.display() + txt.print("\nFuel: ") + util9.print_10s(ship9.fuel) + txt.print(" Cargohold space: ") + txt.print_ub(ship9.cargo_free()) + txt.print("t\n") + } +} + +ship9 { + const ubyte Max_fuel = 70 + const ubyte Fuel_cost = 2 + ubyte Max_cargo = 20 + + ubyte fuel = Max_fuel + uword cash = 1000 ; actually has to be 4 bytes for the ultra rich.... + ubyte[17] cargohold = 0 + + sub init() { + memset(cargohold, len(cargohold), 0) + } + + sub cargo_free() -> ubyte { + ubyte ci + ubyte total = 0 + for ci in 0 to len(cargohold)-1 { + if market9.units[ci]==0 ; tonnes only + total += cargohold[ci] + } + return Max_cargo - total + } +} + +market9 { + ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35] + byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F] + ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0] + ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07] + ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0] + str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers", + "Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"] + + ubyte[17] current_quantity = 0 + uword[17] current_price = 0 + + sub init(ubyte fluct) { + ; Prices and availabilities are influenced by the planet9's economy type + ; (0-7) and a random "fluctuation" byte that was kept within the saved + ; commander position to keep the market9 prices constant over gamesaves. + ; Availabilities must be saved with the game since the player alters them + ; by buying (and selling(?)) + ; + ; Almost all operations are one byte only and overflow "errors" are + ; extremely frequent and exploited. + ; + ; Trade Item prices are held internally in a single byte=true value/4. + ; The decimal point in prices is introduced only when printing them. + ; Internally, all prices are integers. + ; The player's cash is held in four bytes. + ubyte ci + for ci in 0 to len(names)-1 { + word product + byte changing + product = planet9.economy as word * gradients[ci] + changing = fluct & maskbytes[ci] as byte + ubyte q = (basequants[ci] as word + changing - product) as ubyte + if q & $80 + q = 0 ; clip to positive 8-bit + current_quantity[ci] = q & $3f + q = (baseprices[ci] + changing + product) as ubyte + current_price[ci] = q * $0004 + } + current_quantity[16] = 0 ; force nonavailability of Alien Items + } + + sub display() { + ubyte ci + txt.chrout('\n') + planet9.print_name_uppercase() + txt.print(" trade market9:\n COMMODITY / PRICE / AVAIL / IN HOLD\n") + for ci in 0 to len(names)-1 { + util9.print_right(13, names[ci]) + txt.print(" ") + util9.print_10s(current_price[ci]) + txt.print(" ") + txt.print_ub(current_quantity[ci]) + when units[ci] { + 0 -> txt.chrout('t') + 1 -> txt.print("kg") + 2 -> txt.chrout('g') + } + txt.print(" ") + txt.print_ub(ship9.cargohold[ci]) + txt.chrout('\n') + } + } + + sub match(uword nameptr) -> ubyte { + ubyte ci + for ci in 0 to len(names)-1 { + if util9.prefix_matches(nameptr, names[ci]) + return ci + } + return 255 + } +} + +galaxy9 { + const uword GALSIZE = 256 + const uword base0 = $5A4A ; seeds for the first galaxy9 + const uword base1 = $0248 + const uword base2 = $B753 + + str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion" + + ubyte number + + uword[3] seed + + sub init(ubyte galaxy9num) { + number = 1 + planet9.number = 255 + seed = [base0, base1, base2] + repeat galaxy9num-1 { + nextgalaxy9() + } + } + + sub nextgalaxy9() { + seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] + number++ + if number==9 + number = 1 + } + + sub travel_to(ubyte galaxy9num, ubyte system) { + init(galaxy9num) + generate_next_planet9() ; always at least planet9 0 (separate to avoid repeat ubyte overflow) + repeat system { + generate_next_planet9() + } + planet9.name = make_current_planet9_name() + init_market9_for_planet9() + } + + sub init_market9_for_planet9() { + market9.init(lsb(seed[0])+msb(seed[2])) + } + + sub search_closest_planet9(uword nameptr) -> ubyte { + ubyte x = planet9.x + ubyte y = planet9.y + ubyte current_planet9_num = planet9.number + + init(number) + ubyte found = false + ubyte current_closest_pi + ubyte current_distance = 127 + ubyte pi + for pi in 0 to 255 { + generate_next_planet9() + planet9.name = make_current_planet9_name() + if util9.prefix_matches(nameptr, planet9.name) { + ubyte distance = planet9.distance(x, y) + if distance < current_distance { + current_distance = distance + current_closest_pi = pi + found = true + } + } + } + + if found + travel_to(number, current_closest_pi) + else + travel_to(number, current_planet9_num) + + return found + } + + sub local_area() { + ubyte current_planet9 = planet9.number + ubyte px = planet9.x + ubyte py = planet9.y + ubyte pn = 0 + + init(number) + txt.print("\nGalaxy #") + txt.print_ub(number) + txt.print(" - systems in vicinity:\n") + do { + generate_next_planet9() + ubyte distance = planet9.distance(px, py) + if distance <= ship9.Max_fuel { + if distance <= ship9.fuel + txt.chrout('*') + else + txt.chrout('-') + txt.chrout(' ') + planet9.name = make_current_planet9_name() + planet9.display(true) + txt.print(" (") + util9.print_10s(distance) + txt.print(" LY)\n") + } + pn++ + } until pn==0 + + travel_to(number, current_planet9) + } + + ubyte pn_pair1 + ubyte pn_pair2 + ubyte pn_pair3 + ubyte pn_pair4 + ubyte longname + + sub generate_next_planet9() { + determine_planet9_properties() + longname = lsb(seed[0]) & 64 + + ; Always four iterations of random number + pn_pair1 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair2 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair3 = (msb(seed[2]) & 31) * 2 + tweakseed() + pn_pair4 = (msb(seed[2]) & 31) * 2 + tweakseed() + } + + sub make_current_planet9_name() -> str { + ubyte ni = 0 + str name = " " ; max 8 + + if pn_pairs[pn_pair1] != '.' { + name[ni] = pn_pairs[pn_pair1] + ni++ + } + if pn_pairs[pn_pair1+1] != '.' { + name[ni] = pn_pairs[pn_pair1+1] + ni++ + } + if pn_pairs[pn_pair2] != '.' { + name[ni] = pn_pairs[pn_pair2] + ni++ + } + if pn_pairs[pn_pair2+1] != '.' { + name[ni] = pn_pairs[pn_pair2+1] + ni++ + } + if pn_pairs[pn_pair3] != '.' { + name[ni] = pn_pairs[pn_pair3] + ni++ + } + if pn_pairs[pn_pair3+1] != '.' { + name[ni] = pn_pairs[pn_pair3+1] + ni++ + } + + if longname { + if pn_pairs[pn_pair4] != '.' { + name[ni] = pn_pairs[pn_pair4] + ni++ + } + if pn_pairs[pn_pair4+1] != '.' { + name[ni] = pn_pairs[pn_pair4+1] + ni++ + } + } + + name[ni] = 0 + return name + } + + sub determine_planet9_properties() { + ; create the planet9's characteristics + planet9.number++ + planet9.x = msb(seed[1]) + planet9.y = msb(seed[0]) + planet9.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1 + planet9.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0 + if planet9.govtype <= 1 + planet9.economy = (planet9.economy | 2) + planet9.techlevel = (msb(seed[1]) & 3) + (planet9.economy ^ 7) + planet9.techlevel += planet9.govtype >> 1 + if planet9.govtype & 1 + planet9.techlevel++ + planet9.population = 4 * planet9.techlevel + planet9.economy + planet9.population += planet9.govtype + 1 + planet9.productivity = ((planet9.economy ^ 7) + 3) * (planet9.govtype + 4) + planet9.productivity *= planet9.population * 8 + ubyte seed2_msb = msb(seed[2]) + planet9.radius = mkword((seed2_msb & 15) + 11, planet9.x) + planet9.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo + if planet9.species_is_alien { + planet9.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi + planet9.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi + planet9.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) + planet9.species_kind = (planet9.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result + } + + planet9.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] + } + + sub tweakseed() { + uword temp = seed[0] + seed[1] + seed[2] + seed[0] = seed[1] + seed[1] = seed[2] + seed[2] = temp + } + + sub twist(uword x) -> uword { + ubyte xh = msb(x) + ubyte xl = lsb(x) + rol(xh) + rol(xl) + return mkword(xh, xl) + } + + sub debug_seed() { + txt.print("\ngalaxy9 #") + txt.print_ub(number) + txt.print("\ngalaxy9 seed0=") + txt.print_uwhex(galaxy9.seed[0], true) + txt.print("\ngalaxy9 seed1=") + txt.print_uwhex(galaxy9.seed[1], true) + txt.print("\ngalaxy9 seed2=") + txt.print_uwhex(galaxy9.seed[2], true) + txt.chrout('\n') + } +} + +planet9 { + %option force_output + + str[] species_sizes = ["Large", "Fierce", "Small"] + str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"] + str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"] + str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"] + str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship9", "Communist", "Confederacy", "Democracy", "Corporate State"] + str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial", + "Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"] + + str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] + str[] words82 = ["very", "mildly", "most", "reasonably", ""] + str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] + str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] + str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] + str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] + str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] + str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] + str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] + str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] + str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] + str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] + str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] + str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] + str[] words8F = ["\xB0", "The planet9 \xB0", "The world \xB0", "This planet9", "This world"] + str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] + str[] words91 = ["planet9", "world", "place", "little planet9", "dump"] + str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] + str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] + str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] + str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] + str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] + str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] + str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] + str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] + str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] + str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] + str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] + str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] + str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] + str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] + str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] + str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] + str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] + str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] + str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] + + uword[] wordlists = [ + words81, words82, words83, words84, words85, words86, words87, words88, + words89, words8A, words8B, words8C, words8D, words8E, words8F, words90, + words91, words92, words93, words94, words95, words96, words97, words98, + words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0, + wordsA1, wordsA2, wordsA3, wordsA4] + + str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe" + + ubyte[4] goatsoup_rnd = [0, 0, 0, 0] + ubyte[4] goatsoup_seed = [0, 0, 0, 0] + + str name = " " ; 8 max + ubyte number ; starts at 0 in new galaxy9, then increases by 1 for each generated planet9 + ubyte x + ubyte y + ubyte economy + ubyte govtype + ubyte techlevel + ubyte population + uword productivity + uword radius + ubyte species_is_alien ; otherwise "Human Colonials" + ubyte species_size + ubyte species_color + ubyte species_look + ubyte species_kind + + sub set_seed(uword s1, uword s2) { + goatsoup_seed[0] = lsb(s1) + goatsoup_seed[1] = msb(s1) + goatsoup_seed[2] = lsb(s2) + goatsoup_seed[3] = msb(s2) + reset_rnd() + } + + sub reset_rnd() { + goatsoup_rnd[0] = goatsoup_seed[0] + goatsoup_rnd[1] = goatsoup_seed[1] + goatsoup_rnd[2] = goatsoup_seed[2] + goatsoup_rnd[3] = goatsoup_seed[3] + } + + sub random_name() -> str { + ubyte ii + str name = " " ; 8 chars max + ubyte nx = 0 + for ii in 0 to goatsoup_rnd_number() & 3 { + ubyte x = goatsoup_rnd_number() & $3e + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + x++ + if pairs0[x] != '.' { + name[nx] = pairs0[x] + nx++ + } + } + name[nx] = 0 + name[0] |= 32 ; uppercase first letter + return name + } + + sub goatsoup_rnd_number() -> ubyte { + ubyte x = goatsoup_rnd[0] * 2 + uword a = x as uword + goatsoup_rnd[2] + if goatsoup_rnd[0] > 127 + a ++ + goatsoup_rnd[0] = lsb(a) + goatsoup_rnd[2] = x + x = goatsoup_rnd[1] + ubyte ac = x + goatsoup_rnd[3] + msb(a) + goatsoup_rnd[1] = ac + goatsoup_rnd[3] = x + return ac + } + + sub distance(ubyte px, ubyte py) -> ubyte { + uword ax + uword ay + if px>x + ax=px-x + else + ax=x-px + if py>y + ay=py-y + else + ay=y-py + ay /= 2 + ubyte d = sqrt16(ax*ax + ay*ay) + if d>63 + return 255 + return d*4 + } + + sub soup() -> str { + str planet9_result = " " * 160 + uword[6] source_stack + ubyte stack_ptr = 0 + str start_source = "\x8F is \x97." + uword source_ptr = &start_source + uword result_ptr = &planet9_result + + reset_rnd() + recursive_soup() + return planet9_result + + sub recursive_soup() { + repeat { + ubyte c = @(source_ptr) + source_ptr++ + if c == $00 { + @(result_ptr) = 0 + return + } + else if c <= $80 { + @(result_ptr) = c + result_ptr++ + } + else { + if c <= $a4 { + ubyte rnr = goatsoup_rnd_number() + ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC) + source_stack[stack_ptr] = source_ptr + stack_ptr++ + source_ptr = getword(c, wordNr) + recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case + stack_ptr-- + source_ptr = source_stack[stack_ptr] + } else { + if c == $b0 { + @(result_ptr) = name[0] | 32 + result_ptr++ + concat_string(&name + 1) + } + else if c == $b1 { + @(result_ptr) = name[0] | 32 + result_ptr++ + ubyte ni + for ni in 1 to len(name) { + ubyte cc = name[ni] + if cc=='e' or cc=='o' or cc==0 + break + else { + @(result_ptr) = cc + result_ptr++ + } + } + @(result_ptr) = 'i' + result_ptr++ + @(result_ptr) = 'a' + result_ptr++ + @(result_ptr) = 'n' + result_ptr++ + } + else if c == $b2 { + concat_string(random_name()) + } + else { + @(result_ptr) = c + result_ptr++ + } + } + } + } + } + + sub concat_string(uword str_ptr) { + repeat { + ubyte c = @(str_ptr) + if c==0 + break + else { + @(result_ptr) = c + str_ptr++ + result_ptr++ + } + } + } + } + + sub display(ubyte compressed) { + if compressed { + print_name_uppercase() + txt.print(" TL:") + txt.print_ub(techlevel+1) + txt.chrout(' ') + txt.print(econnames[economy]) + txt.chrout(' ') + txt.print(govnames[govtype]) + } else { + txt.print("\n\nSystem: ") + print_name_uppercase() + txt.print("\nPosition: ") + txt.print_ub(x) + txt.chrout('\'') + txt.print_ub(y) + txt.chrout(' ') + txt.chrout('#') + txt.print_ub(number) + txt.print("\nEconomy: ") + txt.print(econnames[economy]) + txt.print("\nGovernment: ") + txt.print(govnames[govtype]) + txt.print("\nTech Level: ") + txt.print_ub(techlevel+1) + txt.print("\nTurnover: ") + txt.print_uw(productivity) + txt.print("\nRadius: ") + txt.print_uw(radius) + txt.print("\nPopulation: ") + txt.print_ub(population >> 3) + txt.print(" Billion\nSpecies: ") + if species_is_alien { + if species_size < len(species_sizes) { + txt.print(species_sizes[species_size]) + txt.chrout(' ') + } + if species_color < len(species_colors) { + txt.print(species_colors[species_color]) + txt.chrout(' ') + } + if species_look < len(species_looks) { + txt.print(species_looks[species_look]) + txt.chrout(' ') + } + if species_kind < len(species_kinds) { + txt.print(species_kinds[species_kind]) + } + } else { + txt.print("Human Colonials") + } + txt.chrout('\n') + txt.print(soup()) + txt.chrout('\n') + } + } + + sub print_name_uppercase() { + ubyte c + for c in name + txt.chrout(c | 32) + } + + asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY { + %asm {{ + sty P8ZP_SCRATCH_REG + sec + sbc #$81 + asl a + tay + lda wordlists,y + sta P8ZP_SCRATCH_W1 + lda wordlists+1,y + sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_REG + asl a + tay + lda (P8ZP_SCRATCH_W1),y + pha + iny + lda (P8ZP_SCRATCH_W1),y + tay + pla + rts + }} + } +} + +util9 { + sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte { + repeat { + ubyte pc = @(prefixptr) + ubyte sc = @(stringptr) + if pc == 0 + return true + ; to lowercase for case insensitive compare: + pc &= 127 + sc &= 127 + if pc != sc + return false + prefixptr++ + stringptr++ + } + return false + } + + sub print_right(ubyte width, uword string) { + repeat width - strlen(string) { + txt.chrout(' ') + } + txt.print(string) + } + + asmsub print_10s(uword value @AY) clobbers(A, X, Y) { + %asm {{ + jsr conv.uword2decimal + lda conv.uword2decimal.decTenThousands + ldy #0 ; have we started printing? + cmp #'0' + beq + + jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decThousands + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decHundreds + cmp #'0' + bne + + cpy #0 + beq ++ ++ jsr c64.CHROUT + iny ++ lda conv.uword2decimal.decTens + jsr c64.CHROUT + lda #'.' + jsr c64.CHROUT + lda conv.uword2decimal.decOnes + jsr c64.CHROUT + rts + }} + } + +}